LibWeb: Keep both horizontal and vertical border radii till painting

This commit is contained in:
MacDue 2022-06-12 15:05:47 +01:00 committed by Linus Groh
parent 2366a73474
commit 28c78b45ca
Notes: sideshowbarker 2024-07-17 10:17:54 +09:00
10 changed files with 133 additions and 84 deletions

View file

@ -110,6 +110,11 @@ struct ContentData {
String alt_text {};
};
struct BorderRadiusData {
CSS::LengthPercentage horizontal_radius { InitialValues::border_radius() };
CSS::LengthPercentage vertical_radius { InitialValues::border_radius() };
};
class ComputedValues {
public:
CSS::Float float_() const { return m_noninherited.float_; }
@ -159,10 +164,10 @@ public:
BorderData const& border_right() const { return m_noninherited.border_right; }
BorderData const& border_bottom() const { return m_noninherited.border_bottom; }
const CSS::LengthPercentage& border_bottom_left_radius() const { return m_noninherited.border_bottom_left_radius; }
const CSS::LengthPercentage& border_bottom_right_radius() const { return m_noninherited.border_bottom_right_radius; }
const CSS::LengthPercentage& border_top_left_radius() const { return m_noninherited.border_top_left_radius; }
const CSS::LengthPercentage& border_top_right_radius() const { return m_noninherited.border_top_right_radius; }
const CSS::BorderRadiusData& border_bottom_left_radius() const { return m_noninherited.border_bottom_left_radius; }
const CSS::BorderRadiusData& border_bottom_right_radius() const { return m_noninherited.border_bottom_right_radius; }
const CSS::BorderRadiusData& border_top_left_radius() const { return m_noninherited.border_top_left_radius; }
const CSS::BorderRadiusData& border_top_right_radius() const { return m_noninherited.border_top_right_radius; }
CSS::Overflow overflow_x() const { return m_noninherited.overflow_x; }
CSS::Overflow overflow_y() const { return m_noninherited.overflow_y; }
@ -237,10 +242,10 @@ protected:
BorderData border_top;
BorderData border_right;
BorderData border_bottom;
LengthPercentage border_bottom_left_radius { InitialValues::border_radius() };
LengthPercentage border_bottom_right_radius { InitialValues::border_radius() };
LengthPercentage border_top_left_radius { InitialValues::border_radius() };
LengthPercentage border_top_right_radius { InitialValues::border_radius() };
BorderRadiusData border_bottom_left_radius;
BorderRadiusData border_bottom_right_radius;
BorderRadiusData border_top_left_radius;
BorderRadiusData border_top_right_radius;
Color background_color { InitialValues::background_color() };
Vector<BackgroundLayerData> background_layers;
CSS::FlexDirection flex_direction { InitialValues::flex_direction() };
@ -304,10 +309,10 @@ public:
void set_overflow_y(CSS::Overflow value) { m_noninherited.overflow_y = value; }
void set_list_style_type(CSS::ListStyleType value) { m_inherited.list_style_type = value; }
void set_display(CSS::Display value) { m_noninherited.display = value; }
void set_border_bottom_left_radius(CSS::LengthPercentage value) { m_noninherited.border_bottom_left_radius = value; }
void set_border_bottom_right_radius(CSS::LengthPercentage value) { m_noninherited.border_bottom_right_radius = value; }
void set_border_top_left_radius(CSS::LengthPercentage value) { m_noninherited.border_top_left_radius = value; }
void set_border_top_right_radius(CSS::LengthPercentage value) { m_noninherited.border_top_right_radius = value; }
void set_border_bottom_left_radius(CSS::BorderRadiusData value) { m_noninherited.border_bottom_left_radius = value; }
void set_border_bottom_right_radius(CSS::BorderRadiusData value) { m_noninherited.border_bottom_right_radius = value; }
void set_border_top_left_radius(CSS::BorderRadiusData value) { m_noninherited.border_top_left_radius = value; }
void set_border_top_right_radius(CSS::BorderRadiusData value) { m_noninherited.border_top_right_radius = value; }
BorderData& border_left() { return m_noninherited.border_left; }
BorderData& border_top() { return m_noninherited.border_top; }
BorderData& border_right() { return m_noninherited.border_right; }

View file

@ -284,14 +284,22 @@ RefPtr<StyleValue> ResolvedCSSStyleDeclaration::style_value_for_property(Layout:
return BorderRadiusShorthandStyleValue::create(top_left_radius.release_nonnull(), top_right_radius.release_nonnull(), bottom_right_radius.release_nonnull(), bottom_left_radius.release_nonnull());
}
// FIXME: The two radius components are not yet stored, as we currently don't actually render them.
case CSS::PropertyID::BorderBottomLeftRadius:
return BorderRadiusStyleValue::create(layout_node.computed_values().border_bottom_left_radius(), layout_node.computed_values().border_bottom_left_radius());
case CSS::PropertyID::BorderBottomRightRadius:
return BorderRadiusStyleValue::create(layout_node.computed_values().border_bottom_right_radius(), layout_node.computed_values().border_bottom_right_radius());
case CSS::PropertyID::BorderTopLeftRadius:
return BorderRadiusStyleValue::create(layout_node.computed_values().border_top_left_radius(), layout_node.computed_values().border_top_left_radius());
case CSS::PropertyID::BorderTopRightRadius:
return BorderRadiusStyleValue::create(layout_node.computed_values().border_top_right_radius(), layout_node.computed_values().border_top_right_radius());
case CSS::PropertyID::BorderBottomLeftRadius: {
auto const& border_radius = layout_node.computed_values().border_bottom_left_radius();
return BorderRadiusStyleValue::create(border_radius.horizontal_radius, border_radius.vertical_radius);
}
case CSS::PropertyID::BorderBottomRightRadius: {
auto const& border_radius = layout_node.computed_values().border_bottom_right_radius();
return BorderRadiusStyleValue::create(border_radius.horizontal_radius, border_radius.vertical_radius);
}
case CSS::PropertyID::BorderTopLeftRadius: {
auto const& border_radius = layout_node.computed_values().border_top_left_radius();
return BorderRadiusStyleValue::create(border_radius.horizontal_radius, border_radius.vertical_radius);
}
case CSS::PropertyID::BorderTopRightRadius: {
auto const& border_radius = layout_node.computed_values().border_top_right_radius();
return BorderRadiusStyleValue::create(border_radius.horizontal_radius, border_radius.vertical_radius);
}
case CSS::PropertyID::BorderTopWidth:
return LengthStyleValue::create(Length::make_px(layout_node.computed_values().border_top().width));
case CSS::PropertyID::BorderRightWidth:

View file

@ -368,21 +368,33 @@ void NodeWithStyle::apply_style(const CSS::StyleProperties& computed_style)
// FIXME: BorderXRadius properties are now BorderRadiusStyleValues, so make use of that.
auto border_bottom_left_radius = computed_style.property(CSS::PropertyID::BorderBottomLeftRadius);
if (border_bottom_left_radius->is_border_radius())
computed_values.set_border_bottom_left_radius(border_bottom_left_radius->as_border_radius().horizontal_radius());
if (border_bottom_left_radius->is_border_radius()) {
computed_values.set_border_bottom_left_radius(
CSS::BorderRadiusData {
border_bottom_left_radius->as_border_radius().horizontal_radius(),
border_bottom_left_radius->as_border_radius().vertical_radius() });
}
auto border_bottom_right_radius = computed_style.property(CSS::PropertyID::BorderBottomRightRadius);
if (border_bottom_right_radius->is_border_radius())
computed_values.set_border_bottom_right_radius(border_bottom_right_radius->as_border_radius().horizontal_radius());
if (border_bottom_right_radius->is_border_radius()) {
computed_values.set_border_bottom_right_radius(
CSS::BorderRadiusData {
border_bottom_right_radius->as_border_radius().horizontal_radius(),
border_bottom_right_radius->as_border_radius().vertical_radius() });
}
auto border_top_left_radius = computed_style.property(CSS::PropertyID::BorderTopLeftRadius);
if (border_top_left_radius->is_border_radius())
computed_values.set_border_top_left_radius(border_top_left_radius->as_border_radius().horizontal_radius());
if (border_top_left_radius->is_border_radius()) {
computed_values.set_border_top_left_radius(
CSS::BorderRadiusData {
border_top_left_radius->as_border_radius().horizontal_radius(),
border_top_left_radius->as_border_radius().vertical_radius() });
}
auto border_top_right_radius = computed_style.property(CSS::PropertyID::BorderTopRightRadius);
if (border_top_right_radius->is_border_radius())
computed_values.set_border_top_right_radius(border_top_right_radius->as_border_radius().horizontal_radius());
if (border_top_right_radius->is_border_radius()) {
computed_values.set_border_top_right_radius(
CSS::BorderRadiusData {
border_top_right_radius->as_border_radius().horizontal_radius(),
border_top_right_radius->as_border_radius().vertical_radius() });
}
computed_values.set_display(computed_style.display());
auto flex_direction = computed_style.flex_direction();

View file

@ -15,7 +15,7 @@
namespace Web::Painting {
// https://www.w3.org/TR/css-backgrounds-3/#backgrounds
void paint_background(PaintContext& context, Layout::NodeWithStyleAndBoxModelMetrics const& layout_node, Gfx::FloatRect const& border_rect, Color background_color, Vector<CSS::BackgroundLayerData> const* background_layers, BorderRadiusData const& border_radius)
void paint_background(PaintContext& context, Layout::NodeWithStyleAndBoxModelMetrics const& layout_node, Gfx::FloatRect const& border_rect, Color background_color, Vector<CSS::BackgroundLayerData> const* background_layers, BorderRadiiData const& border_radii)
{
auto& painter = context.painter();
@ -41,10 +41,10 @@ void paint_background(PaintContext& context, Layout::NodeWithStyleAndBoxModelMet
auto color_rect = border_rect;
if (background_layers && !background_layers->is_empty())
color_rect = get_box(background_layers->last().clip);
// FIXME: Support elliptical corners
Gfx::AntiAliasingPainter aa_painter { painter };
aa_painter.fill_rect_with_rounded_corners(color_rect.to_rounded<int>(),
background_color, border_radius.top_left, border_radius.top_right, border_radius.bottom_right, border_radius.bottom_left);
background_color, border_radii.top_left.horizontal_radius, border_radii.top_right.horizontal_radius, border_radii.bottom_right.horizontal_radius, border_radii.bottom_left.horizontal_radius);
if (!background_layers)
return;

View file

@ -13,6 +13,6 @@
namespace Web::Painting {
void paint_background(PaintContext&, Layout::NodeWithStyleAndBoxModelMetrics const&, Gfx::FloatRect const&, Color background_color, Vector<CSS::BackgroundLayerData> const*, BorderRadiusData const&);
void paint_background(PaintContext&, Layout::NodeWithStyleAndBoxModelMetrics const&, Gfx::FloatRect const&, Color background_color, Vector<CSS::BackgroundLayerData> const*, BorderRadiiData const&);
}

View file

@ -11,37 +11,55 @@
namespace Web::Painting {
BorderRadiusData normalized_border_radius_data(Layout::Node const& node, Gfx::FloatRect const& rect, CSS::LengthPercentage top_left_radius, CSS::LengthPercentage top_right_radius, CSS::LengthPercentage bottom_right_radius, CSS::LengthPercentage bottom_left_radius)
BorderRadiiData normalized_border_radii_data(Layout::Node const& node, Gfx::FloatRect const& rect, CSS::BorderRadiusData top_left_radius, CSS::BorderRadiusData top_right_radius, CSS::BorderRadiusData bottom_right_radius, CSS::BorderRadiusData bottom_left_radius)
{
// FIXME: Some values should be relative to the height() if specified, but which?
// Spec just says "Refer to corresponding dimension of the border box."
// For now, all relative values are relative to the width.
auto width_length = CSS::Length::make_px(rect.width());
auto bottom_left_radius_px = bottom_left_radius.resolved(node, width_length).to_px(node);
auto bottom_right_radius_px = bottom_right_radius.resolved(node, width_length).to_px(node);
auto top_left_radius_px = top_left_radius.resolved(node, width_length).to_px(node);
auto top_right_radius_px = top_right_radius.resolved(node, width_length).to_px(node);
auto height_length = CSS::Length::make_px(rect.height());
BorderRadiusData bottom_left_radius_px {
bottom_left_radius.horizontal_radius.resolved(node, width_length).to_px(node),
bottom_left_radius.vertical_radius.resolved(node, height_length).to_px(node)
};
BorderRadiusData bottom_right_radius_px {
bottom_right_radius.horizontal_radius.resolved(node, width_length).to_px(node),
bottom_right_radius.vertical_radius.resolved(node, height_length).to_px(node)
};
BorderRadiusData top_left_radius_px {
top_left_radius.horizontal_radius.resolved(node, width_length).to_px(node),
top_left_radius.vertical_radius.resolved(node, height_length).to_px(node)
};
BorderRadiusData top_right_radius_px {
top_right_radius.horizontal_radius.resolved(node, width_length).to_px(node),
top_right_radius.vertical_radius.resolved(node, height_length).to_px(node)
};
// Scale overlapping curves according to https://www.w3.org/TR/css-backgrounds-3/#corner-overlap
auto f = 1.0f;
auto width_reciprocal = 1.0f / rect.width();
auto height_reciprocal = 1.0f / rect.height();
f = max(f, width_reciprocal * (top_left_radius_px + top_right_radius_px));
f = max(f, height_reciprocal * (top_right_radius_px + bottom_right_radius_px));
f = max(f, width_reciprocal * (bottom_left_radius_px + bottom_right_radius_px));
f = max(f, height_reciprocal * (top_left_radius_px + bottom_left_radius_px));
f = max(f, width_reciprocal * (top_left_radius_px.horizontal_radius + top_right_radius_px.horizontal_radius));
f = max(f, height_reciprocal * (top_right_radius_px.vertical_radius + bottom_right_radius_px.vertical_radius));
f = max(f, width_reciprocal * (bottom_left_radius_px.horizontal_radius + bottom_right_radius_px.horizontal_radius));
f = max(f, height_reciprocal * (top_left_radius_px.vertical_radius + bottom_left_radius_px.vertical_radius));
f = 1.0f / f;
top_left_radius_px = (int)(top_left_radius_px * f);
top_right_radius_px = (int)(top_right_radius_px * f);
bottom_right_radius_px = (int)(bottom_right_radius_px * f);
bottom_left_radius_px = (int)(bottom_left_radius_px * f);
top_left_radius_px.horizontal_radius *= f;
top_left_radius_px.vertical_radius *= f;
top_right_radius_px.horizontal_radius *= f;
top_right_radius_px.vertical_radius *= f;
bottom_right_radius_px.horizontal_radius *= f;
bottom_right_radius_px.vertical_radius *= f;
bottom_left_radius_px.horizontal_radius *= f;
bottom_left_radius_px.vertical_radius *= f;
return BorderRadiusData { top_left_radius_px, top_right_radius_px, bottom_right_radius_px, bottom_left_radius_px };
return BorderRadiiData { top_left_radius_px, top_right_radius_px, bottom_right_radius_px, bottom_left_radius_px };
}
void paint_border(PaintContext& context, BorderEdge edge, Gfx::FloatRect const& a_rect, BorderRadiusData const& border_radius_data, BordersData const& borders_data)
void paint_border(PaintContext& context, BorderEdge edge, Gfx::FloatRect const& a_rect, BorderRadiiData const& border_radii_data, BordersData const& borders_data)
{
auto rect = a_rect.to_rounded<float>();
@ -132,10 +150,10 @@ void paint_border(PaintContext& context, BorderEdge edge, Gfx::FloatRect const&
float p1_step = 0;
float p2_step = 0;
bool has_top_left_radius = border_radius_data.top_left > 0;
bool has_top_right_radius = border_radius_data.top_right > 0;
bool has_bottom_left_radius = border_radius_data.bottom_left > 0;
bool has_bottom_right_radius = border_radius_data.bottom_right > 0;
bool has_top_left_radius = border_radii_data.top_left.horizontal_radius > 0;
bool has_top_right_radius = border_radii_data.top_right.horizontal_radius > 0;
bool has_bottom_left_radius = border_radii_data.bottom_left.horizontal_radius > 0;
bool has_bottom_right_radius = border_radii_data.bottom_right.horizontal_radius > 0;
switch (edge) {
case BorderEdge::Top:
@ -177,16 +195,15 @@ void paint_border(PaintContext& context, BorderEdge edge, Gfx::FloatRect const&
}
}
void paint_all_borders(PaintContext& context, Gfx::FloatRect const& bordered_rect, BorderRadiusData const& border_radius_data, BordersData const& borders_data)
void paint_all_borders(PaintContext& context, Gfx::FloatRect const& bordered_rect, BorderRadiiData const& border_radii_data, BordersData const& borders_data)
{
auto const border_rect = bordered_rect;
auto const top_left_radius = border_radius_data.top_left;
auto const top_right_radius = border_radius_data.top_right;
auto const bottom_right_radius = border_radius_data.bottom_right;
auto const bottom_left_radius = border_radius_data.bottom_left;
// FIXME: Support elliptical border radii.
auto const top_left_radius = border_radii_data.top_left.horizontal_radius;
auto const top_right_radius = border_radii_data.top_right.horizontal_radius;
auto const bottom_right_radius = border_radii_data.bottom_right.horizontal_radius;
auto const bottom_left_radius = border_radii_data.bottom_left.horizontal_radius;
Gfx::FloatRect top_border_rect = {
border_rect.x() + top_left_radius,
@ -213,10 +230,10 @@ void paint_all_borders(PaintContext& context, Gfx::FloatRect const& bordered_rec
border_rect.height() - top_left_radius - bottom_left_radius
};
Painting::paint_border(context, Painting::BorderEdge::Top, top_border_rect, border_radius_data, borders_data);
Painting::paint_border(context, Painting::BorderEdge::Right, right_border_rect, border_radius_data, borders_data);
Painting::paint_border(context, Painting::BorderEdge::Bottom, bottom_border_rect, border_radius_data, borders_data);
Painting::paint_border(context, Painting::BorderEdge::Left, left_border_rect, border_radius_data, borders_data);
Painting::paint_border(context, Painting::BorderEdge::Top, top_border_rect, border_radii_data, borders_data);
Painting::paint_border(context, Painting::BorderEdge::Right, right_border_rect, border_radii_data, borders_data);
Painting::paint_border(context, Painting::BorderEdge::Bottom, bottom_border_rect, border_radii_data, borders_data);
Painting::paint_border(context, Painting::BorderEdge::Left, left_border_rect, border_radii_data, borders_data);
// Draws a quarter circle clockwise
auto draw_quarter_circle = [&](Gfx::FloatPoint const& from, Gfx::FloatPoint const& to, Gfx::Color color, int thickness) {

View file

@ -12,12 +12,18 @@
namespace Web::Painting {
struct BorderRadiusData {
float top_left { 0 };
float top_right { 0 };
float bottom_right { 0 };
float bottom_left { 0 };
float horizontal_radius { 0 };
float vertical_radius { 0 };
};
BorderRadiusData normalized_border_radius_data(Layout::Node const&, Gfx::FloatRect const&, CSS::LengthPercentage top_left_radius, CSS::LengthPercentage top_right_radius, CSS::LengthPercentage bottom_right_radius, CSS::LengthPercentage bottom_left_radius);
struct BorderRadiiData {
BorderRadiusData top_left;
BorderRadiusData top_right;
BorderRadiusData bottom_right;
BorderRadiusData bottom_left;
};
BorderRadiiData normalized_border_radii_data(Layout::Node const&, Gfx::FloatRect const&, CSS::BorderRadiusData top_left_radius, CSS::BorderRadiusData top_right_radius, CSS::BorderRadiusData bottom_right_radius, CSS::BorderRadiusData bottom_left_radius);
enum class BorderEdge {
Top,
@ -31,7 +37,7 @@ struct BordersData {
CSS::BorderData bottom;
CSS::BorderData left;
};
void paint_border(PaintContext& context, BorderEdge edge, Gfx::FloatRect const& rect, BorderRadiusData const& border_radius_data, BordersData const& borders_data);
void paint_all_borders(PaintContext& context, Gfx::FloatRect const& bordered_rect, BorderRadiusData const& border_radius_data, BordersData const&);
void paint_border(PaintContext& context, BorderEdge edge, Gfx::FloatRect const& rect, BorderRadiiData const& border_radii_data, BordersData const& borders_data);
void paint_all_borders(PaintContext& context, Gfx::FloatRect const& bordered_rect, BorderRadiiData const& border_radii_data, BordersData const&);
}

View file

@ -54,8 +54,8 @@ void InlinePaintable::paint(PaintContext& context, Painting::PaintPhase phase) c
absolute_fragment_rect.set_width(absolute_fragment_rect.width() + extra_end_width);
}
auto border_radius_data = Painting::normalized_border_radius_data(layout_node(), absolute_fragment_rect, top_left_border_radius, top_right_border_radius, bottom_right_border_radius, bottom_left_border_radius);
Painting::paint_background(context, layout_node(), absolute_fragment_rect, computed_values().background_color(), &computed_values().background_layers(), border_radius_data);
auto border_radii_data = Painting::normalized_border_radii_data(layout_node(), absolute_fragment_rect, top_left_border_radius, top_right_border_radius, bottom_right_border_radius, bottom_left_border_radius);
Painting::paint_background(context, layout_node(), absolute_fragment_rect, computed_values().background_color(), &computed_values().background_layers(), border_radii_data);
if (auto computed_box_shadow = computed_values().box_shadow(); !computed_box_shadow.is_empty()) {
Vector<Painting::ShadowData> resolved_box_shadow_data;
@ -106,9 +106,9 @@ void InlinePaintable::paint(PaintContext& context, Painting::PaintPhase phase) c
}
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(layout_node(), bordered_rect, top_left_border_radius, top_right_border_radius, bottom_right_border_radius, bottom_left_border_radius);
auto border_radii_data = Painting::normalized_border_radii_data(layout_node(), 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);
Painting::paint_all_borders(context, bordered_rect, border_radii_data, borders_data);
return IterationDecision::Continue;
});

View file

@ -178,7 +178,7 @@ void PaintableBox::paint_border(PaintContext& context) const
.bottom = computed_values().border_bottom(),
.left = computed_values().border_left(),
};
paint_all_borders(context, absolute_border_box_rect(), normalized_border_radius_data(), borders_data);
paint_all_borders(context, absolute_border_box_rect(), normalized_border_radii_data(), borders_data);
}
void PaintableBox::paint_background(PaintContext& context) const
@ -210,7 +210,7 @@ void PaintableBox::paint_background(PaintContext& context) const
if (computed_values().border_top().width || computed_values().border_right().width || computed_values().border_bottom().width || computed_values().border_left().width)
background_rect = absolute_border_box_rect();
Painting::paint_background(context, layout_box(), background_rect, background_color, background_layers, normalized_border_radius_data());
Painting::paint_background(context, layout_box(), background_rect, background_color, background_layers, normalized_border_radii_data());
}
void PaintableBox::paint_box_shadow(PaintContext& context) const
@ -233,9 +233,9 @@ void PaintableBox::paint_box_shadow(PaintContext& context) const
Painting::paint_box_shadow(context, enclosing_int_rect(absolute_border_box_rect()), resolved_box_shadow_data);
}
BorderRadiusData PaintableBox::normalized_border_radius_data() const
BorderRadiiData PaintableBox::normalized_border_radii_data() const
{
return Painting::normalized_border_radius_data(layout_box(), absolute_border_box_rect(),
return Painting::normalized_border_radii_data(layout_box(), absolute_border_box_rect(),
computed_values().border_top_left_radius(),
computed_values().border_top_right_radius(),
computed_values().border_bottom_right_radius(),

View file

@ -6,6 +6,7 @@
#pragma once
#include <LibWeb/Painting/BorderPainting.h>
#include <LibWeb/Painting/Paintable.h>
namespace Web::Painting {
@ -129,7 +130,7 @@ protected:
virtual Gfx::FloatRect compute_absolute_rect() const;
private:
Painting::BorderRadiusData normalized_border_radius_data() const;
Painting::BorderRadiiData normalized_border_radii_data() const;
OwnPtr<Painting::StackingContext> m_stacking_context;