1
0
mirror of https://github.com/SerenityOS/serenity synced 2024-07-09 03:30:46 +00:00

LibWeb: Resolve backdrop filter length in apply_style()

Instead of resolving lengths used in the backdrop-filter during
painting, we can do that earlier in apply_style().

This change moves us a bit closer to the point when the stacking
context tree will be completely separated from the layout tree :)
This commit is contained in:
Aliaksandr Kalenik 2023-10-12 01:34:20 +02:00 committed by Andreas Kling
parent 6528f6db26
commit 7803dcfcf9
7 changed files with 74 additions and 37 deletions

View File

@ -47,6 +47,34 @@ struct QuotesData {
Vector<Array<String, 2>> strings {};
};
struct ResolvedBackdropFilter {
struct Blur {
float radius;
};
struct DropShadow {
double offset_x;
double offset_y;
double radius;
Color color;
};
struct HueRotate {
float angle_degrees;
};
struct ColorOperation {
Filter::Color::Operation operation;
float amount;
};
using FilterFunction = Variant<Blur, DropShadow, HueRotate, ColorOperation>;
bool is_none() const { return filters.size() == 0; }
Vector<FilterFunction> filters;
};
class InitialValues {
public:
static AspectRatio aspect_ratio() { return AspectRatio { true, {} }; }
@ -71,7 +99,7 @@ public:
static CSS::Display display() { return CSS::Display { CSS::DisplayOutside::Inline, CSS::DisplayInside::Flow }; }
static Color color() { return Color::Black; }
static Color stop_color() { return Color::Black; }
static CSS::BackdropFilter backdrop_filter() { return BackdropFilter::make_none(); }
static CSS::ResolvedBackdropFilter backdrop_filter() { return ResolvedBackdropFilter { .filters = {} }; }
static Color background_color() { return Color::Transparent; }
static CSS::ListStyleType list_style_type() { return CSS::ListStyleType::Disc; }
static CSS::ListStylePosition list_style_position() { return CSS::ListStylePosition::Outside; }
@ -302,7 +330,7 @@ public:
CSS::JustifyContent justify_content() const { return m_noninherited.justify_content; }
CSS::JustifySelf justify_self() const { return m_noninherited.justify_self; }
CSS::JustifyItems justify_items() const { return m_noninherited.justify_items; }
CSS::BackdropFilter const& backdrop_filter() const { return m_noninherited.backdrop_filter; }
CSS::ResolvedBackdropFilter const& backdrop_filter() const { return m_noninherited.backdrop_filter; }
Vector<ShadowData> const& box_shadow() const { return m_noninherited.box_shadow; }
CSS::BoxSizing box_sizing() const { return m_noninherited.box_sizing; }
CSS::Size const& width() const { return m_noninherited.width; }
@ -452,7 +480,7 @@ protected:
CSS::LengthBox inset { InitialValues::inset() };
CSS::LengthBox margin { InitialValues::margin() };
CSS::LengthBox padding { InitialValues::padding() };
CSS::BackdropFilter backdrop_filter { InitialValues::backdrop_filter() };
CSS::ResolvedBackdropFilter backdrop_filter { InitialValues::backdrop_filter() };
BorderData border_left;
BorderData border_top;
BorderData border_right;
@ -565,7 +593,7 @@ public:
void set_list_style_type(CSS::ListStyleType value) { m_inherited.list_style_type = value; }
void set_list_style_position(CSS::ListStylePosition value) { m_inherited.list_style_position = value; }
void set_display(CSS::Display value) { m_noninherited.display = value; }
void set_backdrop_filter(CSS::BackdropFilter backdrop_filter) { m_noninherited.backdrop_filter = move(backdrop_filter); }
void set_backdrop_filter(CSS::ResolvedBackdropFilter backdrop_filter) { m_noninherited.backdrop_filter = move(backdrop_filter); }
void set_border_bottom_left_radius(CSS::BorderRadiusData value) { m_noninherited.border_bottom_left_radius = move(value); }
void set_border_bottom_right_radius(CSS::BorderRadiusData value) { m_noninherited.border_bottom_right_radius = move(value); }
void set_border_top_left_radius(CSS::BorderRadiusData value) { m_noninherited.border_top_left_radius = move(value); }

View File

@ -23,18 +23,6 @@ float Filter::Blur::resolved_radius(Layout::Node const& node) const
return sigma * 2;
}
Filter::DropShadow::Resolved Filter::DropShadow::resolved(Layout::Node const& node) const
{
// The default value for omitted values is missing length values set to 0
// and the missing used color is taken from the color property.
return Resolved {
offset_x.to_px(node).to_double(),
offset_y.to_px(node).to_double(),
radius.has_value() ? radius->to_px(node).to_double() : 0.0,
color.has_value() ? *color : node.computed_values().color()
};
}
float Filter::HueRotate::angle_degrees() const
{
// Default value when omitted is 0deg.

View File

@ -29,13 +29,6 @@ struct DropShadow {
Length offset_y;
Optional<Length> radius {};
Optional<Color> color {};
struct Resolved {
double offset_x;
double offset_y;
double radius;
Color color;
};
Resolved resolved(Layout::Node const&) const;
bool operator==(DropShadow const&) const = default;
};

View File

@ -537,7 +537,35 @@ void NodeWithStyle::apply_style(const CSS::StyleProperties& computed_style)
computed_values.set_flex_shrink(computed_style.flex_shrink());
computed_values.set_order(computed_style.order());
computed_values.set_clip(computed_style.clip());
computed_values.set_backdrop_filter(computed_style.backdrop_filter());
if (computed_style.backdrop_filter().has_filters()) {
CSS::ResolvedBackdropFilter resolved_backdrop_filter;
for (auto& filter : computed_style.backdrop_filter().filters()) {
filter.visit(
[&](CSS::Filter::Blur const& blur) {
resolved_backdrop_filter.filters.append(CSS::ResolvedBackdropFilter::Blur {
.radius = blur.resolved_radius(*this) });
},
[&](CSS::Filter::DropShadow const& drop_shadow) {
// The default value for omitted values is missing length values set to 0
// and the missing used color is taken from the color property.
resolved_backdrop_filter.filters.append(CSS::ResolvedBackdropFilter::DropShadow {
.offset_x = drop_shadow.offset_x.to_px(*this).to_double(),
.offset_y = drop_shadow.offset_y.to_px(*this).to_double(),
.radius = drop_shadow.radius.has_value() ? drop_shadow.radius->to_px(*this).to_double() : 0.0,
.color = drop_shadow.color.has_value() ? *drop_shadow.color : this->computed_values().color() });
},
[&](CSS::Filter::Color const& color_operation) {
resolved_backdrop_filter.filters.append(CSS::ResolvedBackdropFilter::ColorOperation {
.operation = color_operation.operation,
.amount = color_operation.resolved_amount() });
},
[&](CSS::Filter::HueRotate const& hue_rotate) {
resolved_backdrop_filter.filters.append(CSS::ResolvedBackdropFilter::HueRotate { .angle_degrees = hue_rotate.angle_degrees() });
});
}
computed_values.set_backdrop_filter(resolved_backdrop_filter);
}
auto justify_content = computed_style.justify_content();
if (justify_content.has_value())

View File

@ -19,7 +19,7 @@
namespace Web::Painting {
void apply_filter_list(Gfx::Bitmap& target_bitmap, Layout::Node const& node, ReadonlySpan<CSS::FilterFunction> filter_list)
void apply_filter_list(Gfx::Bitmap& target_bitmap, ReadonlySpan<CSS::ResolvedBackdropFilter::FilterFunction> filter_list)
{
auto apply_color_filter = [&](Gfx::ColorFilter const& filter) {
const_cast<Gfx::ColorFilter&>(filter).apply(target_bitmap, target_bitmap.rect(), target_bitmap, target_bitmap.rect());
@ -27,14 +27,14 @@ void apply_filter_list(Gfx::Bitmap& target_bitmap, Layout::Node const& node, Rea
for (auto& filter_function : filter_list) {
// See: https://drafts.fxtf.org/filter-effects-1/#supported-filter-functions
filter_function.visit(
[&](CSS::Filter::Blur const& blur) {
[&](CSS::ResolvedBackdropFilter::Blur const& blur_filter) {
// Applies a Gaussian blur to the input image.
// The passed parameter defines the value of the standard deviation to the Gaussian function.
Gfx::StackBlurFilter filter { target_bitmap };
filter.process_rgba(blur.resolved_radius(node), Color::Transparent);
filter.process_rgba(blur_filter.radius, Color::Transparent);
},
[&](CSS::Filter::Color const& color) {
auto amount = color.resolved_amount();
[&](CSS::ResolvedBackdropFilter::ColorOperation const& color) {
auto amount = color.amount;
auto amount_clamped = clamp(amount, 0.0f, 1.0f);
switch (color.operation) {
case CSS::Filter::Color::Operation::Grayscale: {
@ -86,19 +86,19 @@ void apply_filter_list(Gfx::Bitmap& target_bitmap, Layout::Node const& node, Rea
break;
}
},
[&](CSS::Filter::HueRotate const& hue_rotate) {
[&](CSS::ResolvedBackdropFilter::HueRotate const& hue_rotate) {
// Applies a hue rotation on the input image.
// The passed parameter defines the number of degrees around the color circle the input samples will be adjusted.
// A value of 0deg leaves the input unchanged. Implementations must not normalize this value in order to allow animations beyond 360deg.
apply_color_filter(Gfx::HueRotateFilter { hue_rotate.angle_degrees() });
apply_color_filter(Gfx::HueRotateFilter { hue_rotate.angle_degrees });
},
[&](CSS::Filter::DropShadow const&) {
[&](CSS::ResolvedBackdropFilter::DropShadow const&) {
dbgln("TODO: Implement drop-shadow() filter function!");
});
}
}
void apply_backdrop_filter(PaintContext& context, Layout::Node const& node, CSSPixelRect const& backdrop_rect, BorderRadiiData const& border_radii_data, CSS::BackdropFilter const& backdrop_filter)
void apply_backdrop_filter(PaintContext& context, CSSPixelRect const& backdrop_rect, BorderRadiiData const& border_radii_data, CSS::ResolvedBackdropFilter const& backdrop_filter)
{
// This performs the backdrop filter operation: https://drafts.fxtf.org/filter-effects-2/#backdrop-filter-operation
@ -121,7 +121,7 @@ void apply_backdrop_filter(PaintContext& context, Layout::Node const& node, CSSP
}
auto backdrop_bitmap = maybe_backdrop_bitmap.release_value();
// 2. Apply the backdrop-filters filter operations to the entire contents of T'.
apply_filter_list(*backdrop_bitmap, node, backdrop_filter.filters());
apply_filter_list(*backdrop_bitmap, backdrop_filter.filters);
// FIXME: 3. If element B has any transforms (between B and the Backdrop Root), apply the inverse of those transforms to the contents of T.

View File

@ -12,8 +12,8 @@
namespace Web::Painting {
void apply_filter_list(Gfx::Bitmap& target_bitmap, Layout::Node const& node, ReadonlySpan<CSS::FilterFunction> filter_list);
void apply_filter_list(Gfx::Bitmap& target_bitmap, ReadonlySpan<CSS::ResolvedBackdropFilter::FilterFunction> filter_list);
void apply_backdrop_filter(PaintContext&, Layout::Node const&, CSSPixelRect const&, BorderRadiiData const&, CSS::BackdropFilter const&);
void apply_backdrop_filter(PaintContext&, CSSPixelRect const&, BorderRadiiData const&, CSS::ResolvedBackdropFilter const&);
}

View File

@ -325,7 +325,7 @@ void PaintableBox::paint_backdrop_filter(PaintContext& context) const
{
auto& backdrop_filter = computed_values().backdrop_filter();
if (!backdrop_filter.is_none())
apply_backdrop_filter(context, layout_node(), absolute_border_box_rect(), normalized_border_radii_data(), backdrop_filter);
apply_backdrop_filter(context, absolute_border_box_rect(), normalized_border_radii_data(), backdrop_filter);
}
void PaintableBox::paint_background(PaintContext& context) const