diff options
author | Sam Atkins <atkinssj@serenityos.org> | 2022-02-08 14:48:37 +0000 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2022-02-08 17:45:51 +0100 |
commit | 10c6c77b5c252ac88a5411b8188b8ee4ed190827 (patch) | |
tree | c046fb81adba658592884fa30eeeada4ecf0c16e | |
parent | b51f428165c60120fcf30f8b5e6c785ca1aec16d (diff) | |
download | serenity-10c6c77b5c252ac88a5411b8188b8ee4ed190827.zip |
LibWeb: Render multiple box-shadows
Because why not? :^)
-rw-r--r-- | Userland/Libraries/LibWeb/CSS/ComputedValues.h | 6 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/CSS/ResolvedCSSStyleDeclaration.cpp | 21 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/CSS/StyleProperties.cpp | 29 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/CSS/StyleProperties.h | 2 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/Layout/Box.cpp | 17 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/Layout/InlineNode.cpp | 19 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/Painting/ShadowPainting.cpp | 68 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/Painting/ShadowPainting.h | 4 |
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&); } |