ladybird/Userland/Libraries/LibWeb/Layout/InlineNode.cpp
Sam Atkins 103613a3a9 LibWeb: Incorporate spread-distance into box-shadow rendering
We also pass whether the shadow goes inside or outside the element. Only
outer shadows are rendered currently, and inner ones may want to be
handled separately from them, as they will never interfere with each
other.
2022-02-08 17:45:51 +01:00

113 lines
5.5 KiB
C++

/*
* Copyright (c) 2018-2022, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2021, Sam Atkins <atkinssj@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibGfx/Painter.h>
#include <LibWeb/DOM/Document.h>
#include <LibWeb/DOM/Element.h>
#include <LibWeb/Layout/BlockContainer.h>
#include <LibWeb/Layout/InlineFormattingContext.h>
#include <LibWeb/Layout/InlineNode.h>
#include <LibWeb/Painting/BackgroundPainting.h>
#include <LibWeb/Painting/BorderPainting.h>
#include <LibWeb/Painting/ShadowPainting.h>
namespace Web::Layout {
InlineNode::InlineNode(DOM::Document& document, DOM::Element& element, NonnullRefPtr<CSS::StyleProperties> style)
: Layout::NodeWithStyleAndBoxModelMetrics(document, &element, move(style))
{
set_inline(true);
}
InlineNode::~InlineNode()
{
}
void InlineNode::paint(PaintContext& context, PaintPhase phase)
{
auto& painter = context.painter();
if (phase == PaintPhase::Background) {
auto top_left_border_radius = computed_values().border_top_left_radius();
auto top_right_border_radius = computed_values().border_top_right_radius();
auto bottom_right_border_radius = computed_values().border_bottom_right_radius();
auto bottom_left_border_radius = computed_values().border_bottom_left_radius();
auto containing_block_position_in_absolute_coordinates = containing_block()->absolute_position();
for_each_fragment([&](auto const& fragment) {
Gfx::FloatRect absolute_fragment_rect { containing_block_position_in_absolute_coordinates.translated(fragment.offset()), fragment.size() };
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.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(
layer.color,
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)),
static_cast<int>(layer.spread_distance.resolved_or_zero(*this).to_px(*this)),
layer.placement == CSS::BoxShadowPlacement::Outer ? Painting::BoxShadowPlacement::Outer : Painting::BoxShadowPlacement::Inner);
}
Painting::paint_box_shadow(context, enclosing_int_rect(absolute_fragment_rect), resolved_box_shadow_data);
}
return IterationDecision::Continue;
});
}
if (phase == PaintPhase::Border) {
auto top_left_border_radius = computed_values().border_top_left_radius();
auto top_right_border_radius = computed_values().border_top_right_radius();
auto bottom_right_border_radius = computed_values().border_bottom_right_radius();
auto bottom_left_border_radius = computed_values().border_bottom_left_radius();
auto borders_data = Painting::BordersData {
.top = computed_values().border_top(),
.right = computed_values().border_right(),
.bottom = computed_values().border_bottom(),
.left = computed_values().border_left(),
};
auto containing_block_position_in_absolute_coordinates = containing_block()->absolute_position();
for_each_fragment([&](auto& fragment) {
Gfx::FloatRect absolute_fragment_rect { containing_block_position_in_absolute_coordinates.translated(fragment.offset()), fragment.size() };
auto bordered_rect = absolute_fragment_rect.inflated(borders_data.top.width, borders_data.right.width, borders_data.bottom.width, borders_data.left.width);
auto border_radius_data = Painting::normalized_border_radius_data(*this, bordered_rect, top_left_border_radius, top_right_border_radius, bottom_right_border_radius, bottom_left_border_radius);
Painting::paint_all_borders(context, bordered_rect, border_radius_data, borders_data);
return IterationDecision::Continue;
});
}
if (phase == PaintPhase::Foreground && document().inspected_node() == dom_node()) {
// FIXME: This paints a double-thick border between adjacent fragments, where ideally there
// would be none. Once we implement non-rectangular outlines for the `outline` CSS
// property, we can use that here instead.
for_each_fragment([&](auto& fragment) {
painter.draw_rect(enclosing_int_rect(fragment.absolute_rect()), Color::Magenta);
return IterationDecision::Continue;
});
}
}
template<typename Callback>
void InlineNode::for_each_fragment(Callback callback)
{
// FIXME: This will be slow if the containing block has a lot of fragments!
containing_block()->for_each_fragment([&](auto& fragment) {
if (!is_inclusive_ancestor_of(fragment.layout_node()))
return IterationDecision::Continue;
return callback(fragment);
});
}
}