summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSam Atkins <atkinssj@serenityos.org>2022-02-08 14:48:37 +0000
committerAndreas Kling <kling@serenityos.org>2022-02-08 17:45:51 +0100
commit10c6c77b5c252ac88a5411b8188b8ee4ed190827 (patch)
treec046fb81adba658592884fa30eeeada4ecf0c16e
parentb51f428165c60120fcf30f8b5e6c785ca1aec16d (diff)
downloadserenity-10c6c77b5c252ac88a5411b8188b8ee4ed190827.zip
LibWeb: Render multiple box-shadows
Because why not? :^)
-rw-r--r--Userland/Libraries/LibWeb/CSS/ComputedValues.h6
-rw-r--r--Userland/Libraries/LibWeb/CSS/ResolvedCSSStyleDeclaration.cpp21
-rw-r--r--Userland/Libraries/LibWeb/CSS/StyleProperties.cpp29
-rw-r--r--Userland/Libraries/LibWeb/CSS/StyleProperties.h2
-rw-r--r--Userland/Libraries/LibWeb/Layout/Box.cpp17
-rw-r--r--Userland/Libraries/LibWeb/Layout/InlineNode.cpp19
-rw-r--r--Userland/Libraries/LibWeb/Painting/ShadowPainting.cpp68
-rw-r--r--Userland/Libraries/LibWeb/Painting/ShadowPainting.h4
8 files changed, 104 insertions, 62 deletions
diff --git a/Userland/Libraries/LibWeb/CSS/ComputedValues.h b/Userland/Libraries/LibWeb/CSS/ComputedValues.h
index d07c38ee6d..df0f42bb5a 100644
--- a/Userland/Libraries/LibWeb/CSS/ComputedValues.h
+++ b/Userland/Libraries/LibWeb/CSS/ComputedValues.h
@@ -104,7 +104,7 @@ public:
CSS::AlignItems align_items() const { return m_noninherited.align_items; }
float opacity() const { return m_noninherited.opacity; }
CSS::JustifyContent justify_content() const { return m_noninherited.justify_content; }
- Optional<BoxShadowData> const& box_shadow() const { return m_noninherited.box_shadow; }
+ Vector<BoxShadowData> const& box_shadow() const { return m_noninherited.box_shadow; }
CSS::BoxSizing box_sizing() const { return m_noninherited.box_sizing; }
CSS::LengthPercentage const& width() const { return m_noninherited.width; }
CSS::LengthPercentage const& min_width() const { return m_noninherited.min_width; }
@@ -201,7 +201,7 @@ protected:
CSS::Overflow overflow_x { InitialValues::overflow() };
CSS::Overflow overflow_y { InitialValues::overflow() };
float opacity { InitialValues::opacity() };
- Optional<BoxShadowData> box_shadow {};
+ Vector<BoxShadowData> box_shadow {};
Vector<CSS::Transformation> transformations {};
CSS::BoxSizing box_sizing { InitialValues::box_sizing() };
} m_noninherited;
@@ -255,7 +255,7 @@ public:
void set_align_items(CSS::AlignItems value) { m_noninherited.align_items = value; }
void set_opacity(float value) { m_noninherited.opacity = value; }
void set_justify_content(CSS::JustifyContent value) { m_noninherited.justify_content = value; }
- void set_box_shadow(Optional<BoxShadowData> value) { m_noninherited.box_shadow = move(value); }
+ void set_box_shadow(Vector<BoxShadowData>&& value) { m_noninherited.box_shadow = move(value); }
void set_transformations(Vector<CSS::Transformation> value) { m_noninherited.transformations = move(value); }
void set_box_sizing(CSS::BoxSizing value) { m_noninherited.box_sizing = value; }
diff --git a/Userland/Libraries/LibWeb/CSS/ResolvedCSSStyleDeclaration.cpp b/Userland/Libraries/LibWeb/CSS/ResolvedCSSStyleDeclaration.cpp
index adeafb00ce..72b463d07b 100644
--- a/Userland/Libraries/LibWeb/CSS/ResolvedCSSStyleDeclaration.cpp
+++ b/Userland/Libraries/LibWeb/CSS/ResolvedCSSStyleDeclaration.cpp
@@ -518,12 +518,23 @@ RefPtr<StyleValue> ResolvedCSSStyleDeclaration::style_value_for_property(Layout:
case CSS::PropertyID::JustifyContent:
return IdentifierStyleValue::create(to_css_value_id(layout_node.computed_values().justify_content()));
case CSS::PropertyID::BoxShadow: {
- auto maybe_box_shadow = layout_node.computed_values().box_shadow();
- if (!maybe_box_shadow.has_value())
+ auto box_shadow_layers = layout_node.computed_values().box_shadow();
+ if (box_shadow_layers.is_empty())
return {};
- auto box_shadow_data = maybe_box_shadow.release_value();
- // FIXME: Add extra properties to BoxShadowData so we can include them here!
- return BoxShadowStyleValue::create(box_shadow_data.color, box_shadow_data.offset_x, box_shadow_data.offset_y, box_shadow_data.blur_radius, Length::make_px(0), BoxShadowPlacement::Outer);
+
+ auto make_box_shadow_style_value = [](BoxShadowData const& data) {
+ // FIXME: Add extra properties to BoxShadowData so we can include them here!
+ return BoxShadowStyleValue::create(data.color, data.offset_x, data.offset_y, data.blur_radius, Length::make_px(0), BoxShadowPlacement::Outer);
+ };
+
+ if (box_shadow_layers.size() == 1)
+ return make_box_shadow_style_value(box_shadow_layers.first());
+
+ NonnullRefPtrVector<StyleValue> box_shadow;
+ box_shadow.ensure_capacity(box_shadow_layers.size());
+ for (auto const& layer : box_shadow_layers)
+ box_shadow.append(make_box_shadow_style_value(layer));
+ return StyleValueList::create(move(box_shadow), StyleValueList::Separator::Comma);
}
case CSS::PropertyID::Width:
return style_value_for_length_percentage(layout_node.computed_values().width());
diff --git a/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp b/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp
index 9575ddf138..65d2b054e7 100644
--- a/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp
+++ b/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp
@@ -769,18 +769,35 @@ Optional<CSS::Overflow> StyleProperties::overflow(CSS::PropertyID property_id) c
}
}
-Optional<CSS::BoxShadowData> StyleProperties::box_shadow() const
+Vector<BoxShadowData> StyleProperties::box_shadow() const
{
- auto value_or_error = property(CSS::PropertyID::BoxShadow);
+ auto value_or_error = property(PropertyID::BoxShadow);
if (!value_or_error.has_value())
return {};
auto value = value_or_error.value();
- if (!value->is_box_shadow())
- return {};
- auto& box = value->as_box_shadow();
- return { { box.offset_x(), box.offset_y(), box.blur_radius(), box.color() } };
+ auto make_box_shadow_data = [](BoxShadowStyleValue const& box) {
+ return BoxShadowData { box.offset_x(), box.offset_y(), box.blur_radius(), box.color() };
+ };
+
+ if (value->is_value_list()) {
+ auto& value_list = value->as_value_list();
+
+ Vector<BoxShadowData> box_shadow_data;
+ box_shadow_data.ensure_capacity(value_list.size());
+ for (auto const& layer_value : value_list.values())
+ box_shadow_data.append(make_box_shadow_data(layer_value.as_box_shadow()));
+
+ return box_shadow_data;
+ }
+
+ if (value->is_box_shadow()) {
+ auto& box = value->as_box_shadow();
+ return { make_box_shadow_data(box) };
+ }
+
+ return {};
}
CSS::BoxSizing StyleProperties::box_sizing() const
diff --git a/Userland/Libraries/LibWeb/CSS/StyleProperties.h b/Userland/Libraries/LibWeb/CSS/StyleProperties.h
index 777a7a65c5..4c86749b7a 100644
--- a/Userland/Libraries/LibWeb/CSS/StyleProperties.h
+++ b/Userland/Libraries/LibWeb/CSS/StyleProperties.h
@@ -64,7 +64,7 @@ public:
Optional<CSS::JustifyContent> justify_content() const;
Optional<CSS::Overflow> overflow_x() const;
Optional<CSS::Overflow> overflow_y() const;
- Optional<CSS::BoxShadowData> box_shadow() const;
+ Vector<CSS::BoxShadowData> box_shadow() const;
CSS::BoxSizing box_sizing() const;
Optional<CSS::PointerEvents> pointer_events() const;
diff --git a/Userland/Libraries/LibWeb/Layout/Box.cpp b/Userland/Libraries/LibWeb/Layout/Box.cpp
index a8d68d6693..897e1ae375 100644
--- a/Userland/Libraries/LibWeb/Layout/Box.cpp
+++ b/Userland/Libraries/LibWeb/Layout/Box.cpp
@@ -99,15 +99,18 @@ void Box::paint_background(PaintContext& context)
void Box::paint_box_shadow(PaintContext& context)
{
auto box_shadow_data = computed_values().box_shadow();
- if (!box_shadow_data.has_value())
+ if (box_shadow_data.is_empty())
return;
- auto resolved_box_shadow_data = Painting::BoxShadowData {
- .offset_x = (int)box_shadow_data->offset_x.resolved_or_zero(*this).to_px(*this),
- .offset_y = (int)box_shadow_data->offset_y.resolved_or_zero(*this).to_px(*this),
- .blur_radius = (int)box_shadow_data->blur_radius.resolved_or_zero(*this).to_px(*this),
- .color = box_shadow_data->color
- };
+ Vector<Painting::BoxShadowData> resolved_box_shadow_data;
+ resolved_box_shadow_data.ensure_capacity(box_shadow_data.size());
+ for (auto const& layer : box_shadow_data) {
+ resolved_box_shadow_data.empend(
+ static_cast<int>(layer.offset_x.resolved_or_zero(*this).to_px(*this)),
+ static_cast<int>(layer.offset_y.resolved_or_zero(*this).to_px(*this)),
+ static_cast<int>(layer.blur_radius.resolved_or_zero(*this).to_px(*this)),
+ layer.color);
+ }
Painting::paint_box_shadow(context, enclosing_int_rect(bordered_rect()), resolved_box_shadow_data);
}
diff --git a/Userland/Libraries/LibWeb/Layout/InlineNode.cpp b/Userland/Libraries/LibWeb/Layout/InlineNode.cpp
index afd320d17c..c4ba68df0f 100644
--- a/Userland/Libraries/LibWeb/Layout/InlineNode.cpp
+++ b/Userland/Libraries/LibWeb/Layout/InlineNode.cpp
@@ -43,14 +43,17 @@ void InlineNode::paint(PaintContext& context, PaintPhase phase)
auto border_radius_data = Painting::normalized_border_radius_data(*this, absolute_fragment_rect, top_left_border_radius, top_right_border_radius, bottom_right_border_radius, bottom_left_border_radius);
Painting::paint_background(context, *this, enclosing_int_rect(absolute_fragment_rect), computed_values().background_color(), &computed_values().background_layers(), border_radius_data);
- if (auto computed_box_shadow = computed_values().box_shadow(); computed_box_shadow.has_value()) {
- auto box_shadow_data = Painting::BoxShadowData {
- .offset_x = (int)computed_box_shadow->offset_x.resolved_or_zero(*this).to_px(*this),
- .offset_y = (int)computed_box_shadow->offset_y.resolved_or_zero(*this).to_px(*this),
- .blur_radius = (int)computed_box_shadow->blur_radius.resolved_or_zero(*this).to_px(*this),
- .color = computed_box_shadow->color
- };
- Painting::paint_box_shadow(context, enclosing_int_rect(absolute_fragment_rect), box_shadow_data);
+ if (auto computed_box_shadow = computed_values().box_shadow(); !computed_box_shadow.is_empty()) {
+ Vector<Painting::BoxShadowData> resolved_box_shadow_data;
+ resolved_box_shadow_data.ensure_capacity(computed_box_shadow.size());
+ for (auto const& layer : computed_box_shadow) {
+ resolved_box_shadow_data.empend(
+ static_cast<int>(layer.offset_x.resolved_or_zero(*this).to_px(*this)),
+ static_cast<int>(layer.offset_y.resolved_or_zero(*this).to_px(*this)),
+ static_cast<int>(layer.blur_radius.resolved_or_zero(*this).to_px(*this)),
+ layer.color);
+ }
+ Painting::paint_box_shadow(context, enclosing_int_rect(absolute_fragment_rect), resolved_box_shadow_data);
}
return IterationDecision::Continue;
diff --git a/Userland/Libraries/LibWeb/Painting/ShadowPainting.cpp b/Userland/Libraries/LibWeb/Painting/ShadowPainting.cpp
index 91c09737d5..5fa8eafdf5 100644
--- a/Userland/Libraries/LibWeb/Painting/ShadowPainting.cpp
+++ b/Userland/Libraries/LibWeb/Painting/ShadowPainting.cpp
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
- * Copyright (c) 2021, Sam Atkins <atkinssj@serenityos.org>
+ * Copyright (c) 2021-2022, Sam Atkins <atkinssj@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@@ -13,42 +13,50 @@
namespace Web::Painting {
-void paint_box_shadow(PaintContext& context, Gfx::IntRect const& content_rect, BoxShadowData const& box_shadow_data)
+void paint_box_shadow(PaintContext& context, Gfx::IntRect const& content_rect, Vector<BoxShadowData> const& box_shadow_layers)
{
- Gfx::IntRect bitmap_rect = {
- 0,
- 0,
- content_rect.width() + 4 * box_shadow_data.blur_radius,
- content_rect.height() + 4 * box_shadow_data.blur_radius
- };
-
- Gfx::IntPoint blur_rect_position = {
- content_rect.x() - 2 * box_shadow_data.blur_radius + box_shadow_data.offset_x,
- content_rect.y() - 2 * box_shadow_data.blur_radius + box_shadow_data.offset_y
- };
-
- if (bitmap_rect.is_empty())
+ if (box_shadow_layers.is_empty())
return;
- auto bitmap_or_error = Gfx::Bitmap::try_create(Gfx::BitmapFormat::BGRA8888, bitmap_rect.size());
- if (bitmap_or_error.is_error()) {
- dbgln("Unable to allocate temporary bitmap for box-shadow rendering: {}", bitmap_or_error.error());
- return;
- }
- auto new_bitmap = bitmap_or_error.release_value_but_fixme_should_propagate_errors();
+ // Note: Box-shadow layers are ordered front-to-back, so we paint them in reverse
+ for (int layer_index = box_shadow_layers.size() - 1; layer_index >= 0; layer_index--) {
+ auto& box_shadow_data = box_shadow_layers[layer_index];
+
+ Gfx::IntRect bitmap_rect = {
+ 0,
+ 0,
+ content_rect.width() + 4 * box_shadow_data.blur_radius,
+ content_rect.height() + 4 * box_shadow_data.blur_radius
+ };
+
+ Gfx::IntPoint blur_rect_position = {
+ content_rect.x() - 2 * box_shadow_data.blur_radius + box_shadow_data.offset_x,
+ content_rect.y() - 2 * box_shadow_data.blur_radius + box_shadow_data.offset_y
+ };
- Gfx::Painter painter(*new_bitmap);
- painter.fill_rect({ { 2 * box_shadow_data.blur_radius, 2 * box_shadow_data.blur_radius }, content_rect.size() }, box_shadow_data.color);
+ if (bitmap_rect.is_empty())
+ return;
- Gfx::FastBoxBlurFilter filter(*new_bitmap);
- filter.apply_three_passes(box_shadow_data.blur_radius);
+ auto bitmap_or_error = Gfx::Bitmap::try_create(Gfx::BitmapFormat::BGRA8888, bitmap_rect.size());
+ if (bitmap_or_error.is_error()) {
+ dbgln("Unable to allocate temporary bitmap for box-shadow rendering: {}", bitmap_or_error.error());
+ return;
+ }
+ auto new_bitmap = bitmap_or_error.release_value_but_fixme_should_propagate_errors();
- Gfx::DisjointRectSet rect_set;
- rect_set.add(bitmap_rect);
- auto shattered = rect_set.shatter({ content_rect.location() - blur_rect_position, content_rect.size() });
+ Gfx::Painter painter(*new_bitmap);
+ painter.fill_rect({ { 2 * box_shadow_data.blur_radius, 2 * box_shadow_data.blur_radius }, content_rect.size() }, box_shadow_data.color);
- for (auto& rect : shattered.rects())
- context.painter().blit(rect.location() + blur_rect_position, *new_bitmap, rect);
+ Gfx::FastBoxBlurFilter filter(*new_bitmap);
+ filter.apply_three_passes(box_shadow_data.blur_radius);
+
+ Gfx::DisjointRectSet rect_set;
+ rect_set.add(bitmap_rect);
+ auto shattered = rect_set.shatter({ content_rect.location() - blur_rect_position, content_rect.size() });
+
+ for (auto& rect : shattered.rects())
+ context.painter().blit(rect.location() + blur_rect_position, *new_bitmap, rect);
+ }
}
}
diff --git a/Userland/Libraries/LibWeb/Painting/ShadowPainting.h b/Userland/Libraries/LibWeb/Painting/ShadowPainting.h
index 5926b539fd..4484a76f8c 100644
--- a/Userland/Libraries/LibWeb/Painting/ShadowPainting.h
+++ b/Userland/Libraries/LibWeb/Painting/ShadowPainting.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021, Sam Atkins <atkinssj@serenityos.org>
+ * Copyright (c) 2021-2022, Sam Atkins <atkinssj@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@@ -18,6 +18,6 @@ struct BoxShadowData {
Gfx::Color color;
};
-void paint_box_shadow(PaintContext&, Gfx::IntRect const&, BoxShadowData const&);
+void paint_box_shadow(PaintContext&, Gfx::IntRect const&, Vector<BoxShadowData> const&);
}