mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-09-03 08:08:43 +00:00
LibWeb/SVG: Apply SVGFeBlendElement
blend mode
Previously, the blend mode was always assumed to be `normal`.
This commit is contained in:
parent
22225af994
commit
1dd3608960
Notes:
github-actions[bot]
2025-08-06 13:22:18 +00:00
Author: https://github.com/tcl3
Commit: 1dd3608960
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/5737
Reviewed-by: https://github.com/AtkinsSJ
Reviewed-by: https://github.com/gmta ✅
8 changed files with 176 additions and 7 deletions
|
@ -7,6 +7,7 @@
|
|||
#include <LibWeb/Bindings/SVGFEBlendElementPrototype.h>
|
||||
#include <LibWeb/CSS/Parser/Parser.h>
|
||||
#include <LibWeb/Layout/Node.h>
|
||||
#include <LibWeb/Painting/Blending.h>
|
||||
#include <LibWeb/SVG/SVGAnimatedEnumeration.h>
|
||||
#include <LibWeb/SVG/SVGFEBlendElement.h>
|
||||
|
||||
|
@ -33,6 +34,24 @@ void SVGFEBlendElement::visit_edges(Cell::Visitor& visitor)
|
|||
visitor.visit(m_in2);
|
||||
}
|
||||
|
||||
void SVGFEBlendElement::attribute_changed(FlyString const& name, Optional<String> const& old_value, Optional<String> const& new_value, Optional<FlyString> const& namespace_)
|
||||
{
|
||||
Base::attribute_changed(name, old_value, new_value, namespace_);
|
||||
|
||||
if (name == SVG::AttributeNames::mode) {
|
||||
auto parse_mix_blend_mode = [](Optional<String> const& value) -> Optional<CSS::MixBlendMode> {
|
||||
if (!value.has_value())
|
||||
return {};
|
||||
auto keyword = CSS::keyword_from_string(*value);
|
||||
if (!keyword.has_value())
|
||||
return {};
|
||||
return CSS::keyword_to_mix_blend_mode(*keyword);
|
||||
};
|
||||
|
||||
m_mode = parse_mix_blend_mode(new_value);
|
||||
}
|
||||
}
|
||||
|
||||
GC::Ref<SVGAnimatedString> SVGFEBlendElement::in1()
|
||||
{
|
||||
if (!m_in1)
|
||||
|
@ -49,10 +68,14 @@ GC::Ref<SVGAnimatedString> SVGFEBlendElement::in2()
|
|||
return *m_in2;
|
||||
}
|
||||
|
||||
GC::Ref<SVGAnimatedEnumeration> SVGFEBlendElement::mode() const
|
||||
Gfx::CompositingAndBlendingOperator SVGFEBlendElement::mode() const
|
||||
{
|
||||
// FIXME: Resolve the actual value from AttributeName::mode.
|
||||
return SVGAnimatedEnumeration::create(realm(), 1);
|
||||
return Painting::mix_blend_mode_to_compositing_and_blending_operator(m_mode.value_or(CSS::MixBlendMode::Normal));
|
||||
}
|
||||
|
||||
GC::Ref<SVGAnimatedEnumeration> SVGFEBlendElement::mode_for_bindings() const
|
||||
{
|
||||
return SVGAnimatedEnumeration::create(realm(), to_underlying(mode()));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -24,7 +24,9 @@ public:
|
|||
|
||||
GC::Ref<SVGAnimatedString> in1();
|
||||
GC::Ref<SVGAnimatedString> in2();
|
||||
GC::Ref<SVGAnimatedEnumeration> mode() const;
|
||||
|
||||
Gfx::CompositingAndBlendingOperator mode() const;
|
||||
GC::Ref<SVGAnimatedEnumeration> mode_for_bindings() const;
|
||||
|
||||
private:
|
||||
SVGFEBlendElement(DOM::Document&, DOM::QualifiedName);
|
||||
|
@ -32,8 +34,12 @@ private:
|
|||
virtual void initialize(JS::Realm&) override;
|
||||
virtual void visit_edges(Cell::Visitor&) override;
|
||||
|
||||
virtual void attribute_changed(FlyString const& name, Optional<String> const& old_value, Optional<String> const& new_value, Optional<FlyString> const& namespace_) override;
|
||||
|
||||
GC::Ptr<SVGAnimatedString> m_in1;
|
||||
GC::Ptr<SVGAnimatedString> m_in2;
|
||||
|
||||
Optional<CSS::MixBlendMode> m_mode;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ interface SVGFEBlendElement : SVGElement {
|
|||
|
||||
readonly attribute SVGAnimatedString in1;
|
||||
readonly attribute SVGAnimatedString in2;
|
||||
readonly attribute SVGAnimatedEnumeration mode;
|
||||
[ImplementedAs=mode_for_bindings] readonly attribute SVGAnimatedEnumeration mode;
|
||||
};
|
||||
|
||||
SVGFEBlendElement includes SVGFilterPrimitiveStandardAttributes;
|
||||
|
|
|
@ -118,8 +118,7 @@ Optional<Gfx::Filter> SVGFilterElement::gfx_filter()
|
|||
auto foreground = resolve_input_filter(blend_primitive->in1()->base_val());
|
||||
auto background = resolve_input_filter(blend_primitive->in2()->base_val());
|
||||
|
||||
// FIXME: Actually resolve the blend mode
|
||||
auto blend_mode = Gfx::CompositingAndBlendingOperator::Normal;
|
||||
auto blend_mode = blend_primitive.mode();
|
||||
|
||||
root_filter = Gfx::Filter::blend(background, foreground, blend_mode);
|
||||
update_result_map(*blend_primitive);
|
||||
|
|
5
Tests/LibWeb/Ref/expected/svg/blend-filter-ref.html
Normal file
5
Tests/LibWeb/Ref/expected/svg/blend-filter-ref.html
Normal file
|
@ -0,0 +1,5 @@
|
|||
<!DOCTYPE html>
|
||||
<svg width="100" height="100" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="0" y="0" width="100" height="100" fill="green"/>
|
||||
<circle cx="50" cy="50" r="50" fill="black" />
|
||||
</svg>
|
13
Tests/LibWeb/Ref/input/svg/blend-filter.html
Normal file
13
Tests/LibWeb/Ref/input/svg/blend-filter.html
Normal file
|
@ -0,0 +1,13 @@
|
|||
<!DOCTYPE html>
|
||||
<link rel="match" href="../../expected/svg/blend-filter-ref.html" />
|
||||
<meta name="fuzzy" content="0-1;0-35">
|
||||
<svg width="100" height="100" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<filter id="blendFilter">
|
||||
<feFlood flood-color="green" />
|
||||
<feBlend in2="SourceGraphic" mode="darken" result="blended" />
|
||||
</filter>
|
||||
</defs>
|
||||
<rect x="0" y="0" width="100" height="100" fill="blue" />
|
||||
<circle cx="50" cy="50" r="50" fill="red" filter="url(#blendFilter)" />
|
||||
</svg>
|
|
@ -0,0 +1,41 @@
|
|||
Harness status: OK
|
||||
|
||||
Found 35 tests
|
||||
|
||||
18 Pass
|
||||
17 Fail
|
||||
Pass SVGFEBlendElement.prototype.mode, getter, initial value
|
||||
Pass SVGFEBlendElement.prototype.mode, getter, invalid value
|
||||
Pass SVGFEBlendElement.prototype.mode, getter, numeric value for "normal"
|
||||
Pass SVGFEBlendElement.prototype.mode, getter, numeric value for "multiply"
|
||||
Pass SVGFEBlendElement.prototype.mode, getter, numeric value for "screen"
|
||||
Pass SVGFEBlendElement.prototype.mode, getter, numeric value for "darken"
|
||||
Pass SVGFEBlendElement.prototype.mode, getter, numeric value for "lighten"
|
||||
Pass SVGFEBlendElement.prototype.mode, getter, numeric value for "overlay"
|
||||
Pass SVGFEBlendElement.prototype.mode, getter, numeric value for "color-dodge"
|
||||
Pass SVGFEBlendElement.prototype.mode, getter, numeric value for "color-burn"
|
||||
Pass SVGFEBlendElement.prototype.mode, getter, numeric value for "hard-light"
|
||||
Pass SVGFEBlendElement.prototype.mode, getter, numeric value for "soft-light"
|
||||
Pass SVGFEBlendElement.prototype.mode, getter, numeric value for "difference"
|
||||
Pass SVGFEBlendElement.prototype.mode, getter, numeric value for "exclusion"
|
||||
Pass SVGFEBlendElement.prototype.mode, getter, numeric value for "hue"
|
||||
Pass SVGFEBlendElement.prototype.mode, getter, numeric value for "saturation"
|
||||
Pass SVGFEBlendElement.prototype.mode, getter, numeric value for "color"
|
||||
Pass SVGFEBlendElement.prototype.mode, getter, numeric value for "luminosity"
|
||||
Fail SVGFEBlendElement.prototype.mode, setter, invalid value
|
||||
Fail SVGFEBlendElement.prototype.mode, setter, numeric value for "normal"
|
||||
Fail SVGFEBlendElement.prototype.mode, setter, numeric value for "multiply"
|
||||
Fail SVGFEBlendElement.prototype.mode, setter, numeric value for "screen"
|
||||
Fail SVGFEBlendElement.prototype.mode, setter, numeric value for "darken"
|
||||
Fail SVGFEBlendElement.prototype.mode, setter, numeric value for "lighten"
|
||||
Fail SVGFEBlendElement.prototype.mode, setter, numeric value for "overlay"
|
||||
Fail SVGFEBlendElement.prototype.mode, setter, numeric value for "color-dodge"
|
||||
Fail SVGFEBlendElement.prototype.mode, setter, numeric value for "color-burn"
|
||||
Fail SVGFEBlendElement.prototype.mode, setter, numeric value for "hard-light"
|
||||
Fail SVGFEBlendElement.prototype.mode, setter, numeric value for "soft-light"
|
||||
Fail SVGFEBlendElement.prototype.mode, setter, numeric value for "difference"
|
||||
Fail SVGFEBlendElement.prototype.mode, setter, numeric value for "exclusion"
|
||||
Fail SVGFEBlendElement.prototype.mode, setter, numeric value for "hue"
|
||||
Fail SVGFEBlendElement.prototype.mode, setter, numeric value for "saturation"
|
||||
Fail SVGFEBlendElement.prototype.mode, setter, numeric value for "color"
|
||||
Fail SVGFEBlendElement.prototype.mode, setter, numeric value for "luminosity"
|
|
@ -0,0 +1,82 @@
|
|||
<!DOCTYPE HTML>
|
||||
<title>SVGFEBlendElement.prototype.mode</title>
|
||||
<link rel="help" href="https://drafts.fxtf.org/filter-effects/#InterfaceSVGFEBlendElement">
|
||||
<link rel="help" href="https://svgwg.org/svg2-draft/types.html#InterfaceSVGAnimatedEnumeration">
|
||||
<script src="../../resources/testharness.js"></script>
|
||||
<script src="../../resources/testharnessreport.js"></script>
|
||||
<script>
|
||||
// This test checks the use of SVGAnimatedEnumeration for 'mode' on SVGFEBlendElement.
|
||||
|
||||
const svgNs = "http://www.w3.org/2000/svg";
|
||||
|
||||
test(function() {
|
||||
var element = document.createElementNS(svgNs, "feBlend");
|
||||
assert_equals(element.mode.baseVal, SVGFEBlendElement.SVG_FEBLEND_MODE_NORMAL);
|
||||
assert_false(element.hasAttribute("mode"));
|
||||
}, document.title + ", getter, initial value");
|
||||
|
||||
test(function() {
|
||||
var element = document.createElementNS(svgNs, "feBlend");
|
||||
element.setAttribute("mode", "not-a-valid-value");
|
||||
assert_equals(element.mode.baseVal, SVGFEBlendElement.SVG_FEBLEND_MODE_NORMAL);
|
||||
assert_true(element.hasAttribute("mode"));
|
||||
assert_equals(element.getAttribute("mode"), "not-a-valid-value");
|
||||
}, document.title + ", getter, invalid value");
|
||||
|
||||
const enumerationMap = [
|
||||
[ "normal", "NORMAL" ],
|
||||
[ "multiply", "MULTIPLY" ],
|
||||
[ "screen", "SCREEN" ],
|
||||
[ "darken", "DARKEN" ],
|
||||
[ "lighten", "LIGHTEN" ],
|
||||
[ "overlay", "OVERLAY" ],
|
||||
[ "color-dodge", "COLOR_DODGE" ],
|
||||
[ "color-burn", "COLOR_BURN" ],
|
||||
[ "hard-light", "HARD_LIGHT" ],
|
||||
[ "soft-light", "SOFT_LIGHT" ],
|
||||
[ "difference", "DIFFERENCE" ],
|
||||
[ "exclusion", "EXCLUSION" ],
|
||||
[ "hue", "HUE" ],
|
||||
[ "saturation", "SATURATION" ],
|
||||
[ "color", "COLOR" ],
|
||||
[ "luminosity", "LUMINOSITY" ],
|
||||
];
|
||||
|
||||
for (let [enumeration, value_suffix] of enumerationMap) {
|
||||
test(function() {
|
||||
let value = SVGFEBlendElement["SVG_FEBLEND_MODE_" + value_suffix];
|
||||
var element = document.createElementNS(svgNs, "feBlend");
|
||||
element.setAttribute("mode", enumeration);
|
||||
assert_equals(element.mode.baseVal, value);
|
||||
}, document.title + ", getter, numeric value for \"" + enumeration + "\"");
|
||||
}
|
||||
|
||||
test(function() {
|
||||
var element = document.createElementNS(svgNs, "feBlend");
|
||||
element.setAttribute("mode", "lighten");
|
||||
assert_equals(element.mode.baseVal, SVGFEBlendElement.SVG_FEBLEND_MODE_LIGHTEN);
|
||||
assert_equals(element.getAttribute('mode'), "lighten");
|
||||
|
||||
assert_throws_js(TypeError, function() { element.mode.baseVal = 17; });
|
||||
assert_equals(element.mode.baseVal, SVGFEBlendElement.SVG_FEBLEND_MODE_LIGHTEN);
|
||||
assert_equals(element.getAttribute('mode'), "lighten");
|
||||
|
||||
assert_throws_js(TypeError, function() { element.mode.baseVal = -1; });
|
||||
assert_equals(element.mode.baseVal, SVGFEBlendElement.SVG_FEBLEND_MODE_LIGHTEN);
|
||||
assert_equals(element.getAttribute('mode'), "lighten");
|
||||
|
||||
assert_throws_js(TypeError, function() { element.mode.baseVal = 0; });
|
||||
assert_equals(element.mode.baseVal, SVGFEBlendElement.SVG_FEBLEND_MODE_LIGHTEN);
|
||||
assert_equals(element.getAttribute('mode'), "lighten");
|
||||
}, document.title + ", setter, invalid value");
|
||||
|
||||
for (let [enumeration, value_suffix] of enumerationMap) {
|
||||
test(function() {
|
||||
let value = SVGFEBlendElement["SVG_FEBLEND_MODE_" + value_suffix];
|
||||
var element = document.createElementNS(svgNs, "feBlend");
|
||||
element.mode.baseVal = value;
|
||||
assert_equals(element.mode.baseVal, value);
|
||||
assert_equals(element.getAttribute('mode'), enumeration);
|
||||
}, document.title + ", setter, numeric value for \"" + enumeration + "\"");
|
||||
}
|
||||
</script>
|
Loading…
Add table
Add a link
Reference in a new issue