LibWeb: Use rounding instead of enclosing_int_rect() when painting

By using enclosing_int_rect(), borders and backgrounds of boxes were
sometimes 1 pixel off, making things slightly larger than they should
be. Fix this by using to_rounded() instead of enclosing_int_rect().

There's definitely more of these type of issues lurking in the code,
and we'll get to them in time.
This commit is contained in:
Andreas Kling 2022-03-29 15:43:28 +02:00
parent e1cf51b0bd
commit 0de488749f
Notes: sideshowbarker 2024-07-17 16:34:26 +09:00
5 changed files with 18 additions and 20 deletions

View File

@ -14,7 +14,7 @@
namespace Web::Painting {
// https://www.w3.org/TR/css-backgrounds-3/#backgrounds
void paint_background(PaintContext& context, Layout::NodeWithStyleAndBoxModelMetrics const& layout_node, Gfx::IntRect 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, BorderRadiusData const& border_radius)
{
auto& painter = context.painter();
@ -41,7 +41,7 @@ void paint_background(PaintContext& context, Layout::NodeWithStyleAndBoxModelMet
if (background_layers && !background_layers->is_empty())
color_rect = get_box(background_layers->last().clip);
// FIXME: Support elliptical corners
painter.fill_rect_with_rounded_corners(color_rect, background_color, border_radius.top_left, border_radius.top_right, border_radius.bottom_right, border_radius.bottom_left);
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);
if (!background_layers)
return;
@ -57,14 +57,14 @@ void paint_background(PaintContext& context, Layout::NodeWithStyleAndBoxModelMet
// Clip
auto clip_rect = get_box(layer.clip);
painter.save();
painter.add_clip_rect(clip_rect);
painter.add_clip_rect(clip_rect.to_rounded<int>());
Gfx::IntRect background_positioning_area;
Gfx::FloatRect background_positioning_area;
// Attachment and Origin
switch (layer.attachment) {
case CSS::BackgroundAttachment::Fixed:
background_positioning_area = layout_node.root().browsing_context().viewport_rect();
background_positioning_area = layout_node.root().browsing_context().viewport_rect().to_type<float>();
break;
case CSS::BackgroundAttachment::Local:
case CSS::BackgroundAttachment::Scroll:
@ -76,15 +76,15 @@ void paint_background(PaintContext& context, Layout::NodeWithStyleAndBoxModelMet
Gfx::IntRect image_rect;
switch (layer.size_type) {
case CSS::BackgroundSize::Contain: {
float max_width_ratio = (float)background_positioning_area.width() / (float)image.width();
float max_height_ratio = (float)background_positioning_area.height() / (float)image.height();
float max_width_ratio = background_positioning_area.width() / image.width();
float max_height_ratio = background_positioning_area.height() / image.height();
float ratio = min(max_width_ratio, max_height_ratio);
image_rect.set_size(roundf(image.width() * ratio), roundf(image.height() * ratio));
break;
}
case CSS::BackgroundSize::Cover: {
float max_width_ratio = (float)background_positioning_area.width() / (float)image.width();
float max_height_ratio = (float)background_positioning_area.height() / (float)image.height();
float max_width_ratio = background_positioning_area.width() / image.width();
float max_height_ratio = background_positioning_area.height() / image.height();
float ratio = max(max_width_ratio, max_height_ratio);
image_rect.set_size(roundf(image.width() * ratio), roundf(image.height() * ratio));
break;
@ -176,7 +176,7 @@ void paint_background(PaintContext& context, Layout::NodeWithStyleAndBoxModelMet
x_step = image_rect.width();
repeat_x = false;
} else {
int space = background_positioning_area.width() % image_rect.width();
float space = fmodf(background_positioning_area.width(), image_rect.width());
x_step = image_rect.width() + ((float)space / (float)(whole_images - 1));
repeat_x = true;
}
@ -207,7 +207,7 @@ void paint_background(PaintContext& context, Layout::NodeWithStyleAndBoxModelMet
y_step = image_rect.height();
repeat_y = false;
} else {
int space = background_positioning_area.height() % image_rect.height();
float space = fmodf(background_positioning_area.height(), image_rect.height());
y_step = image_rect.height() + ((float)space / (float)(whole_images - 1));
repeat_y = true;
}

View File

@ -14,6 +14,6 @@
namespace Web::Painting {
void paint_background(PaintContext&, Layout::NodeWithStyleAndBoxModelMetrics const&, Gfx::IntRect 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*, BorderRadiusData const&);
}

View File

@ -43,9 +43,7 @@ BorderRadiusData normalized_border_radius_data(Layout::Node const& node, Gfx::Fl
void paint_border(PaintContext& context, BorderEdge edge, Gfx::FloatRect const& a_rect, BorderRadiusData const& border_radius_data, BordersData const& borders_data)
{
// FIXME: This is a hack that snaps the incoming rect to integer pixel values before painting each side.
// This needs a more general solution.
auto rect = enclosing_int_rect(a_rect).to_type<float>();
auto rect = a_rect.to_rounded<float>();
const auto& border_data = [&] {
switch (edge) {

View File

@ -55,7 +55,7 @@ void InlinePaintable::paint(PaintContext& context, Painting::PaintPhase phase) c
}
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(), enclosing_int_rect(absolute_fragment_rect), computed_values().background_color(), &computed_values().background_layers(), border_radius_data);
Painting::paint_background(context, layout_node(), 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::ShadowData> resolved_box_shadow_data;

View File

@ -184,13 +184,13 @@ void PaintableBox::paint_background(PaintContext& context) const
if (layout_box().is_body() && document().html_element()->should_use_body_background_properties())
return;
Gfx::IntRect background_rect;
Gfx::FloatRect background_rect;
Color background_color = computed_values().background_color();
auto* background_layers = &computed_values().background_layers();
if (layout_box().is_root_element()) {
// CSS 2.1 Appendix E.2: If the element is a root element, paint the background over the entire canvas.
background_rect = context.viewport_rect();
background_rect = context.viewport_rect().to_type<float>();
// Section 2.11.2: If the computed value of background-image on the root element is none and its background-color is transparent,
// user agents must instead propagate the computed values of the background properties from that elements first HTML BODY child element.
@ -199,13 +199,13 @@ void PaintableBox::paint_background(PaintContext& context) const
background_color = document().background_color(context.palette());
}
} else {
background_rect = enclosing_int_rect(absolute_padding_box_rect());
background_rect = absolute_padding_box_rect();
}
// HACK: If the Box has a border, use the bordered_rect to paint the background.
// This way if we have a border-radius there will be no gap between the filling and actual border.
if (computed_values().border_top().width || computed_values().border_right().width || computed_values().border_bottom().width || computed_values().border_left().width)
background_rect = enclosing_int_rect(absolute_border_box_rect());
background_rect = absolute_border_box_rect();
Painting::paint_background(context, layout_box(), background_rect, background_color, background_layers, normalized_border_radius_data());
}