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:
parent
6528f6db26
commit
7803dcfcf9
Notes:
sideshowbarker
2024-07-17 03:14:39 +09:00
Author: https://github.com/kalenikaliaksandr Commit: https://github.com/SerenityOS/serenity/commit/7803dcfcf9 Pull-request: https://github.com/SerenityOS/serenity/pull/21420
7 changed files with 74 additions and 37 deletions
|
@ -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); }
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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-filter’s 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’.
|
||||
|
||||
|
|
|
@ -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&);
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue