mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-09-02 15:46:33 +00:00
LibWeb/SVG: Implement resolution for a subset of SVG filters
This commit is contained in:
parent
5d85959f5f
commit
635adc8aa7
Notes:
github-actions[bot]
2025-07-09 17:08:27 +00:00
Author: https://github.com/ananas-dev
Commit: 635adc8aa7
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/5028
Reviewed-by: https://github.com/gmta ✅
10 changed files with 161 additions and 17 deletions
|
@ -1,11 +1,15 @@
|
|||
/*
|
||||
* Copyright (c) 2025, Jelle Raaijmakers <jelle@ladybird.org>
|
||||
* Copyright (c) 2025, Lucien Fiorini <lucienfiorini@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibWeb/Bindings/SVGFilterElementPrototype.h>
|
||||
#include <LibWeb/CSS/Parser/Parser.h>
|
||||
#include <LibWeb/SVG/SVGFEBlendElement.h>
|
||||
#include <LibWeb/SVG/SVGFEFloodElement.h>
|
||||
#include <LibWeb/SVG/SVGFEGaussianBlurElement.h>
|
||||
#include <LibWeb/SVG/SVGFilterElement.h>
|
||||
|
||||
namespace Web::SVG {
|
||||
|
@ -23,6 +27,12 @@ void SVGFilterElement::initialize(JS::Realm& realm)
|
|||
Base::initialize(realm);
|
||||
}
|
||||
|
||||
void SVGFilterElement::visit_edges(Cell::Visitor& visitor)
|
||||
{
|
||||
Base::visit_edges(visitor);
|
||||
SVGURIReferenceMixin::visit_edges(visitor);
|
||||
}
|
||||
|
||||
void SVGFilterElement::apply_presentational_hints(GC::Ref<CSS::CascadedProperties> cascaded_properties) const
|
||||
{
|
||||
Base::apply_presentational_hints(cascaded_properties);
|
||||
|
@ -63,6 +73,82 @@ void SVGFilterElement::attribute_changed(FlyString const& name, Optional<String>
|
|||
m_primitive_units = AttributeParser::parse_units(value.value_or({}));
|
||||
}
|
||||
|
||||
Gfx::Filter SVGFilterElement::gfx_filter()
|
||||
{
|
||||
HashMap<String, Gfx::Filter> result_map;
|
||||
Optional<Gfx::Filter> root_filter;
|
||||
|
||||
// https://www.w3.org/TR/filter-effects-1/#element-attrdef-filter-primitive-in
|
||||
auto resolve_input_filter = [&](String const& name) -> Optional<Gfx::Filter> {
|
||||
// TODO: Add missing ones.
|
||||
if (name == "SourceGraphic"sv)
|
||||
return {};
|
||||
if (name == "SourceAlpha"sv) {
|
||||
float matrix[20] = {
|
||||
0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0,
|
||||
0, 0, 0, 1, 0
|
||||
};
|
||||
return Gfx::Filter::color_matrix(matrix);
|
||||
}
|
||||
|
||||
auto filter_from_map = result_map.get(name).copy();
|
||||
|
||||
if (filter_from_map.has_value())
|
||||
return filter_from_map;
|
||||
|
||||
return root_filter;
|
||||
};
|
||||
|
||||
for_each_child([&](auto& node) {
|
||||
if (is<SVGFEFloodElement>(node)) {
|
||||
auto& flood_primitive = static_cast<SVGFEFloodElement&>(node);
|
||||
|
||||
root_filter = Gfx::Filter::flood(flood_primitive.flood_color(), flood_primitive.flood_opacity());
|
||||
|
||||
auto result = flood_primitive.result()->base_val();
|
||||
if (!result.is_empty()) {
|
||||
result_map.set(result, *root_filter);
|
||||
}
|
||||
} else if (is<SVGFEBlendElement>(node)) {
|
||||
auto& blend_primitive = static_cast<SVGFEBlendElement&>(node);
|
||||
|
||||
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;
|
||||
|
||||
root_filter = Gfx::Filter::blend(background, foreground, blend_mode);
|
||||
|
||||
auto result = blend_primitive.result()->base_val();
|
||||
if (!result.is_empty()) {
|
||||
result_map.set(result, *root_filter);
|
||||
}
|
||||
} else if (is<SVGFEGaussianBlurElement>(node)) {
|
||||
auto& blur_primitive = static_cast<SVGFEGaussianBlurElement&>(node);
|
||||
|
||||
auto input = resolve_input_filter(blur_primitive.in1()->base_val());
|
||||
|
||||
auto radius_x = blur_primitive.std_deviation_x()->base_val();
|
||||
auto radius_y = blur_primitive.std_deviation_y()->base_val();
|
||||
|
||||
root_filter = Gfx::Filter::blur(radius_x, radius_y, input);
|
||||
|
||||
auto result = blur_primitive.result()->base_val();
|
||||
if (!result.is_empty()) {
|
||||
result_map.set(result, *root_filter);
|
||||
}
|
||||
}
|
||||
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
|
||||
return *root_filter;
|
||||
}
|
||||
|
||||
// https://drafts.fxtf.org/filter-effects/#element-attrdef-filter-filterunits
|
||||
GC::Ref<SVGAnimatedEnumeration> SVGFilterElement::filter_units() const
|
||||
{
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <LibGfx/Filter.h>
|
||||
#include <LibWeb/SVG/AttributeParser.h>
|
||||
#include <LibWeb/SVG/SVGAnimatedEnumeration.h>
|
||||
#include <LibWeb/SVG/SVGAnimatedLength.h>
|
||||
|
@ -30,6 +31,8 @@ public:
|
|||
|
||||
virtual void attribute_changed(FlyString const& name, Optional<String> const& old_value, Optional<String> const& value, Optional<FlyString> const& namespace_) override;
|
||||
|
||||
Gfx::Filter gfx_filter();
|
||||
|
||||
GC::Ref<SVGAnimatedEnumeration> filter_units() const;
|
||||
GC::Ref<SVGAnimatedEnumeration> primitive_units() const;
|
||||
GC::Ref<SVGAnimatedLength> x() const;
|
||||
|
@ -41,6 +44,7 @@ private:
|
|||
SVGFilterElement(DOM::Document&, DOM::QualifiedName);
|
||||
|
||||
virtual void initialize(JS::Realm&) override;
|
||||
virtual void visit_edges(Cell::Visitor&) override;
|
||||
|
||||
Optional<SVGUnits> m_filter_units {};
|
||||
Optional<SVGUnits> m_primitive_units {};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue