1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 04:27:45 +00:00

LibGfx+Everywhere: Change Gfx::Rect to be endpoint exclusive

Previously, calling `.right()` on a `Gfx::Rect` would return the last
column's coordinate still inside the rectangle, or `left + width - 1`.
This is called 'endpoint inclusive' and does not make a lot of sense for
`Gfx::Rect<float>` where a rectangle of width 5 at position (0, 0) would
return 4 as its right side. This same problem exists for `.bottom()`.

This changes `Gfx::Rect` to be endpoint exclusive, which gives us the
nice property that `width = right - left` and `height = bottom - top`.
It enables us to treat `Gfx::Rect<int>` and `Gfx::Rect<float>` exactly
the same.

All users of `Gfx::Rect` have been updated accordingly.
This commit is contained in:
Jelle Raaijmakers 2023-05-22 00:41:18 +02:00 committed by Andreas Kling
parent b7f4363791
commit f391ccfe53
88 changed files with 524 additions and 518 deletions

View file

@ -60,8 +60,8 @@ Gfx::FloatSize RadialGradientStyleValue::resolve_size(Layout::Node const& node,
auto const distance_from = [&](float v, float a, float b, auto distance_function) {
return distance_function(fabs(a - v), fabs(b - v));
};
auto x_dist = distance_from(center.x(), size.left(), size.right(), distance_function);
auto y_dist = distance_from(center.y(), size.top(), size.bottom(), distance_function);
auto x_dist = distance_from(center.x(), size.left(), size.right() - 1, distance_function);
auto y_dist = distance_from(center.y(), size.top(), size.bottom() - 1, distance_function);
if (m_properties.ending_shape == EndingShape::Circle) {
auto dist = distance_function(x_dist, y_dist);
return Gfx::FloatSize { dist, dist };
@ -80,20 +80,20 @@ Gfx::FloatSize RadialGradientStyleValue::resolve_size(Layout::Node const& node,
auto const corner_distance = [&](auto distance_compare, Gfx::FloatPoint& corner) {
auto top_left_distance = size.top_left().distance_from(center);
auto top_right_distance = size.top_right().distance_from(center);
auto bottom_right_distance = size.bottom_right().distance_from(center);
auto bottom_left_distance = size.bottom_left().distance_from(center);
auto top_right_distance = size.top_right().moved_left(1).distance_from(center);
auto bottom_right_distance = size.bottom_right().translated(-1).distance_from(center);
auto bottom_left_distance = size.bottom_left().moved_up(1).distance_from(center);
auto distance = top_left_distance;
if (distance_compare(top_right_distance, distance)) {
corner = size.top_right();
corner = size.top_right().translated(-1, 0);
distance = top_right_distance;
}
if (distance_compare(bottom_right_distance, distance)) {
corner = size.top_right();
corner = size.top_right().translated(-1, 0);
distance = bottom_right_distance;
}
if (distance_compare(bottom_left_distance, distance)) {
corner = size.top_right();
corner = size.top_right().translated(-1, 0);
distance = bottom_left_distance;
}
return distance;

View file

@ -706,7 +706,7 @@ void BlockFormattingContext::place_block_level_element_in_normal_flow_vertically
CSSPixels clearance_y_in_root = 0;
for (auto const& floating_box : float_side.current_boxes) {
auto floating_box_rect_in_root = margin_box_rect_in_ancestor_coordinate_space(floating_box.box, root(), m_state);
clearance_y_in_root = max(clearance_y_in_root, floating_box_rect_in_root.bottom() + 1);
clearance_y_in_root = max(clearance_y_in_root, floating_box_rect_in_root.bottom());
}
// Then, convert the clearance Y to a coordinate relative to the containing block of `child_box`.
@ -762,8 +762,8 @@ static void measure_scrollable_overflow(LayoutState const& state, Box const& box
auto child_rect = absolute_content_rect(box, state);
child_rect.inflate(child_state.border_box_top(), child_state.border_box_right(), child_state.border_box_bottom(), child_state.border_box_left());
bottom_edge = max(bottom_edge, child_rect.bottom());
right_edge = max(right_edge, child_rect.right());
bottom_edge = max(bottom_edge, child_rect.bottom() - 1);
right_edge = max(right_edge, child_rect.right() - 1);
if (box.computed_values().overflow_x() == CSS::Overflow::Hidden && box.computed_values().overflow_y() == CSS::Overflow::Hidden)
return;
@ -874,7 +874,9 @@ void BlockFormattingContext::layout_floating_box(Box const& box, BlockContainer
// We're looking for the innermost preceding float that intersects vertically with `box`.
for (auto& preceding_float : side_data.current_boxes.in_reverse()) {
auto const preceding_float_rect = margin_box_rect_in_ancestor_coordinate_space(preceding_float.box, root(), m_state);
if (!preceding_float_rect.contains_vertically(y_in_root))
// FIXME: the line below is for compatibility with a fixed `Gfx::FloatRect` bug where `.bottom()` was subtracted by one;
// replace with !rect.contains_vertically() and update the test cases accordingly.
if (y_in_root < preceding_float_rect.top() || y_in_root > preceding_float_rect.bottom() - 1.f)
continue;
// We found a preceding float that intersects vertically with the current float.
// Now we need to find out if there's enough inline-axis space to stack them next to each other.
@ -1007,7 +1009,9 @@ BlockFormattingContext::SpaceUsedByFloats BlockFormattingContext::space_used_by_
auto const& floating_box_state = m_state.get(floating_box.box);
// NOTE: The floating box is *not* in the final horizontal position yet, but the size and vertical position is valid.
auto rect = margin_box_rect_in_ancestor_coordinate_space(floating_box.box, root(), m_state);
if (rect.contains_vertically(y.value())) {
// FIXME: the line below is for compatibility with a fixed `Gfx::FloatRect` bug where `.bottom()` was subtracted by one;
// replace with rect.contains_vertically() and update the test cases accordingly.
if (y.value() >= rect.top() && y.value() <= rect.bottom() - 1.f) {
CSSPixels offset_from_containing_block_chain_margins_between_here_and_root = 0;
for (auto const* containing_block = floating_box.box->containing_block(); containing_block && containing_block != &root(); containing_block = containing_block->containing_block()) {
auto const& containing_block_state = m_state.get(*containing_block);
@ -1026,7 +1030,9 @@ BlockFormattingContext::SpaceUsedByFloats BlockFormattingContext::space_used_by_
auto const& floating_box_state = m_state.get(floating_box.box);
// NOTE: The floating box is *not* in the final horizontal position yet, but the size and vertical position is valid.
auto rect = margin_box_rect_in_ancestor_coordinate_space(floating_box.box, root(), m_state);
if (rect.contains_vertically(y.value())) {
// FIXME: the line below is for compatibility with a fixed `Gfx::FloatRect` bug where `.bottom()` was subtracted by one;
// replace with rect.contains_vertically() and update the test cases accordingly.
if (y.value() >= rect.top() && y.value() <= rect.bottom() - 1.f) {
CSSPixels offset_from_containing_block_chain_margins_between_here_and_root = 0;
for (auto const* containing_block = floating_box.box->containing_block(); containing_block && containing_block != &root(); containing_block = containing_block->containing_block()) {
auto const& containing_block_state = m_state.get(*containing_block);

View file

@ -358,7 +358,7 @@ CSSPixels FormattingContext::compute_auto_height_for_block_formatting_context_ro
// NOTE: Floating box coordinates are relative to their own containing block,
// which may or may not be the BFC root.
auto margin_box = margin_box_rect_in_ancestor_coordinate_space(*floating_box, root, m_state);
CSSPixels floating_box_bottom_margin_edge = margin_box.bottom() + 1;
CSSPixels floating_box_bottom_margin_edge = margin_box.bottom();
if (!bottom.has_value() || floating_box_bottom_margin_edge > bottom.value())
bottom = floating_box_bottom_margin_edge;
}

View file

@ -329,7 +329,6 @@ bool InlineFormattingContext::any_floats_intrude_at_y(CSSPixels y) const
bool InlineFormattingContext::can_fit_new_line_at_y(CSSPixels y) const
{
auto top_intrusions = parent().intrusion_by_floats_into_box(containing_block(), y);
auto bottom_intrusions = parent().intrusion_by_floats_into_box(containing_block(), y + containing_block().line_height() - 1);

View file

@ -309,11 +309,11 @@ void paint_background(PaintContext& context, Layout::NodeWithStyleAndBoxModelMet
image.resolve_for_size(layout_node, image_rect.size());
while (image_y <= css_clip_rect.bottom()) {
while (image_y < css_clip_rect.bottom()) {
image_rect.set_y(image_y);
auto image_x = initial_image_x;
while (image_x <= css_clip_rect.right()) {
while (image_x < css_clip_rect.right()) {
image_rect.set_x(image_x);
auto image_device_rect = context.rounded_device_rect(image_rect);
if (image_device_rect != last_image_device_rect && image_device_rect.intersects(context.device_viewport_rect()))

View file

@ -85,13 +85,13 @@ void paint_border(PaintContext& context, BorderEdge edge, DevicePixelRect const&
auto points_for_edge = [](BorderEdge edge, DevicePixelRect const& rect) -> Points {
switch (edge) {
case BorderEdge::Top:
return { rect.top_left(), rect.top_right() };
return { rect.top_left(), rect.top_right().moved_left(1) };
case BorderEdge::Right:
return { rect.top_right(), rect.bottom_right() };
return { rect.top_right().moved_left(1), rect.bottom_right().translated(-1) };
case BorderEdge::Bottom:
return { rect.bottom_left(), rect.bottom_right() };
return { rect.bottom_left().moved_up(1), rect.bottom_right().translated(-1) };
default: // Edge::Left
return { rect.top_left(), rect.bottom_left() };
return { rect.top_left(), rect.bottom_left().moved_up(1) };
}
};
@ -365,13 +365,13 @@ void paint_all_borders(PaintContext& context, CSSPixelRect const& bordered_rect,
blit_corner(border_rect.top_left().to_type<int>(), top_left.as_rect(), pick_corner_color(borders_data.top, borders_data.left));
if (top_right)
blit_corner(border_rect.top_right().to_type<int>().translated(-top_right.horizontal_radius + 1, 0), top_right.as_rect().translated(corner_mask_rect.width().value() - top_right.horizontal_radius, 0), pick_corner_color(borders_data.top, borders_data.right));
blit_corner(border_rect.top_right().to_type<int>().translated(-top_right.horizontal_radius, 0), top_right.as_rect().translated(corner_mask_rect.width().value() - top_right.horizontal_radius, 0), pick_corner_color(borders_data.top, borders_data.right));
if (bottom_right)
blit_corner(border_rect.bottom_right().to_type<int>().translated(-bottom_right.horizontal_radius + 1, -bottom_right.vertical_radius + 1), bottom_right.as_rect().translated(corner_mask_rect.width().value() - bottom_right.horizontal_radius, corner_mask_rect.height().value() - bottom_right.vertical_radius), pick_corner_color(borders_data.bottom, borders_data.right));
blit_corner(border_rect.bottom_right().to_type<int>().translated(-bottom_right.horizontal_radius, -bottom_right.vertical_radius), bottom_right.as_rect().translated(corner_mask_rect.width().value() - bottom_right.horizontal_radius, corner_mask_rect.height().value() - bottom_right.vertical_radius), pick_corner_color(borders_data.bottom, borders_data.right));
if (bottom_left)
blit_corner(border_rect.bottom_left().to_type<int>().translated(0, -bottom_left.vertical_radius + 1), bottom_left.as_rect().translated(0, corner_mask_rect.height().value() - bottom_left.vertical_radius), pick_corner_color(borders_data.bottom, borders_data.left));
blit_corner(border_rect.bottom_left().to_type<int>().translated(0, -bottom_left.vertical_radius), bottom_left.as_rect().translated(0, corner_mask_rect.height().value() - bottom_left.vertical_radius), pick_corner_color(borders_data.bottom, borders_data.left));
}
}

View file

@ -43,7 +43,7 @@ ErrorOr<BorderRadiusCornerClipper> BorderRadiusCornerClipper::create(PaintContex
.top_right = top_right,
.bottom_right = bottom_right,
.bottom_left = bottom_left },
.page_locations = { .top_left = border_rect.top_left(), .top_right = border_rect.top_right().translated(-top_right.horizontal_radius + 1, 0), .bottom_right = border_rect.bottom_right().translated(-bottom_right.horizontal_radius + 1, -bottom_right.vertical_radius + 1), .bottom_left = border_rect.bottom_left().translated(0, -bottom_left.vertical_radius + 1) },
.page_locations = { .top_left = border_rect.top_left(), .top_right = border_rect.top_right().translated(-top_right.horizontal_radius, 0), .bottom_right = border_rect.bottom_right().translated(-bottom_right.horizontal_radius, -bottom_right.vertical_radius), .bottom_left = border_rect.bottom_left().translated(0, -bottom_left.vertical_radius) },
.bitmap_locations = { .top_left = { 0, 0 }, .top_right = { corners_bitmap_size.width() - top_right.horizontal_radius, 0 }, .bottom_right = { corners_bitmap_size.width() - bottom_right.horizontal_radius, corners_bitmap_size.height() - bottom_right.vertical_radius }, .bottom_left = { 0, corners_bitmap_size.height() - bottom_left.vertical_radius } },
.corner_bitmap_size = corners_bitmap_size
};

View file

@ -450,16 +450,16 @@ static void paint_text_decoration(PaintContext& context, Gfx::Painter& painter,
return;
case CSS::TextDecorationLine::Underline:
line_start_point = context.rounded_device_point(fragment_box.top_left().translated(0, baseline + 2));
line_end_point = context.rounded_device_point(fragment_box.top_right().translated(0, baseline + 2));
line_end_point = context.rounded_device_point(fragment_box.top_right().translated(-1, baseline + 2));
break;
case CSS::TextDecorationLine::Overline:
line_start_point = context.rounded_device_point(fragment_box.top_left().translated(0, baseline - glyph_height));
line_end_point = context.rounded_device_point(fragment_box.top_right().translated(0, baseline - glyph_height));
line_end_point = context.rounded_device_point(fragment_box.top_right().translated(-1, baseline - glyph_height));
break;
case CSS::TextDecorationLine::LineThrough: {
auto x_height = font.x_height();
line_start_point = context.rounded_device_point(fragment_box.top_left().translated(0, baseline - x_height * 0.5f));
line_end_point = context.rounded_device_point(fragment_box.top_right().translated(0, baseline - x_height * 0.5f));
line_end_point = context.rounded_device_point(fragment_box.top_right().translated(-1, baseline - x_height * 0.5f));
break;
}
case CSS::TextDecorationLine::Blink:
@ -603,7 +603,7 @@ void PaintableWithLines::paint(PaintContext& context, PaintPhase phase) const
context.painter().draw_rect(context.enclosing_device_rect(fragment_absolute_rect).to_type<int>(), Color::Green);
context.painter().draw_line(
context.rounded_device_point(fragment_absolute_rect.top_left().translated(0, fragment.baseline())).to_type<int>(),
context.rounded_device_point(fragment_absolute_rect.top_right().translated(0, fragment.baseline())).to_type<int>(), Color::Red);
context.rounded_device_point(fragment_absolute_rect.top_right().translated(-1, fragment.baseline())).to_type<int>(), Color::Red);
}
if (is<Layout::TextNode>(fragment.layout_node()))
paint_text_fragment(context, static_cast<Layout::TextNode const&>(fragment.layout_node()), fragment, phase);
@ -715,7 +715,7 @@ Optional<HitTestResult> PaintableWithLines::hit_test(CSSPixelPoint position, Hit
// The best candidate is either the end of the line above, the beginning of the line below, or the beginning or end of the current line.
// We arbitrarily choose to consider the end of the line above and ignore the beginning of the line below.
// If we knew the direction of selection, we could make a better choice.
if (fragment_absolute_rect.bottom() <= position.y()) { // fully below the fragment
if (fragment_absolute_rect.bottom() - 1 <= position.y()) { // fully below the fragment
last_good_candidate = HitTestResult { const_cast<Paintable&>(*fragment.layout_node().paintable()), fragment.start() + fragment.length() };
} else if (fragment_absolute_rect.top() <= position.y()) { // vertically within the fragment
if (position.x() < fragment_absolute_rect.left()) { // left of the fragment

View file

@ -234,11 +234,10 @@ void paint_box_shadow(PaintContext& context, CSSPixelRect const& content_rect, B
auto top_start = inner_bounding_rect.top() - blurred_edge_thickness;
auto bottom_start = inner_bounding_rect.top() + inner_bounding_rect.height() + (blurred_edge_thickness - horizontal_edge_width);
// Note: The +1s in a few of the following translations are due to the -1s Gfx::Rect::right() and Gfx::Rect::bottom().
auto top_left_corner_blit_pos = inner_bounding_rect.top_left().translated(-blurred_edge_thickness, -blurred_edge_thickness);
auto top_right_corner_blit_pos = inner_bounding_rect.top_right().translated(-top_right_corner_size.width() + 1 + double_radius, -blurred_edge_thickness);
auto bottom_left_corner_blit_pos = inner_bounding_rect.bottom_left().translated(-blurred_edge_thickness, -bottom_left_corner_size.height() + 1 + double_radius);
auto bottom_right_corner_blit_pos = inner_bounding_rect.bottom_right().translated(-bottom_right_corner_size.width() + 1 + double_radius, -bottom_right_corner_size.height() + 1 + double_radius);
auto top_right_corner_blit_pos = inner_bounding_rect.top_right().translated(-top_right_corner_size.width() + double_radius, -blurred_edge_thickness);
auto bottom_left_corner_blit_pos = inner_bounding_rect.bottom_left().translated(-blurred_edge_thickness, -bottom_left_corner_size.height() + double_radius);
auto bottom_right_corner_blit_pos = inner_bounding_rect.bottom_right().translated(-bottom_right_corner_size.width() + double_radius, -bottom_right_corner_size.height() + double_radius);
auto paint_shadow = [&](DevicePixelRect clip_rect) {
Gfx::PainterStateSaver save { painter };
@ -253,15 +252,15 @@ void paint_box_shadow(PaintContext& context, CSSPixelRect const& content_rect, B
painter.blit(bottom_right_corner_blit_pos.to_type<int>(), shadow_bitmap, bottom_right_corner_rect.to_type<int>());
// Horizontal edges
for (auto x = inner_bounding_rect.left() + (bottom_left_corner_size.width() - double_radius); x <= inner_bounding_rect.right() - (bottom_right_corner_size.width() - double_radius); ++x)
for (auto x = inner_bounding_rect.left() + (bottom_left_corner_size.width() - double_radius); x < inner_bounding_rect.right() - (bottom_right_corner_size.width() - double_radius); ++x)
painter.blit({ x, bottom_start }, shadow_bitmap, bottom_edge_rect.to_type<int>());
for (auto x = inner_bounding_rect.left() + (top_left_corner_size.width() - double_radius); x <= inner_bounding_rect.right() - (top_right_corner_size.width() - double_radius); ++x)
for (auto x = inner_bounding_rect.left() + (top_left_corner_size.width() - double_radius); x < inner_bounding_rect.right() - (top_right_corner_size.width() - double_radius); ++x)
painter.blit({ x, top_start }, shadow_bitmap, top_edge_rect.to_type<int>());
// Vertical edges
for (auto y = inner_bounding_rect.top() + (top_right_corner_size.height() - double_radius); y <= inner_bounding_rect.bottom() - (bottom_right_corner_size.height() - double_radius); ++y)
for (auto y = inner_bounding_rect.top() + (top_right_corner_size.height() - double_radius); y < inner_bounding_rect.bottom() - (bottom_right_corner_size.height() - double_radius); ++y)
painter.blit({ right_start, y }, shadow_bitmap, right_edge_rect.to_type<int>());
for (auto y = inner_bounding_rect.top() + (top_left_corner_size.height() - double_radius); y <= inner_bounding_rect.bottom() - (bottom_left_corner_size.height() - double_radius); ++y)
for (auto y = inner_bounding_rect.top() + (top_left_corner_size.height() - double_radius); y < inner_bounding_rect.bottom() - (bottom_left_corner_size.height() - double_radius); ++y)
painter.blit({ left_start, y }, shadow_bitmap, left_edge_rect.to_type<int>());
};
@ -300,13 +299,13 @@ void paint_box_shadow(PaintContext& context, CSSPixelRect const& content_rect, B
paint_shadow({ 0, 0, really_large_number, device_content_rect.top() });
// Everything below content_rect, including sides
paint_shadow({ 0, device_content_rect.bottom() + 1, really_large_number, really_large_number });
paint_shadow({ 0, device_content_rect.bottom(), really_large_number, really_large_number });
// Everything directly to the left of content_rect
paint_shadow({ 0, device_content_rect.top(), device_content_rect.left(), device_content_rect.height() });
// Everything directly to the right of content_rect
paint_shadow({ device_content_rect.right() + 1, device_content_rect.top(), really_large_number, device_content_rect.height() });
paint_shadow({ device_content_rect.right(), device_content_rect.top(), really_large_number, device_content_rect.height() });
if (top_left_corner) {
// Inside the top left corner (the part outside the border radius)
@ -316,19 +315,19 @@ void paint_box_shadow(PaintContext& context, CSSPixelRect const& content_rect, B
if (top_right_corner) {
// Inside the top right corner (the part outside the border radius)
auto top_right = top_right_corner.as_rect().to_type<DevicePixels>().translated(device_content_rect.top_right().translated(-top_right_corner.horizontal_radius + 1, 0));
auto top_right = top_right_corner.as_rect().to_type<DevicePixels>().translated(device_content_rect.top_right().translated(-top_right_corner.horizontal_radius, 0));
paint_shadow(top_right);
}
if (bottom_right_corner) {
// Inside the bottom right corner (the part outside the border radius)
auto bottom_right = bottom_right_corner.as_rect().to_type<DevicePixels>().translated(device_content_rect.bottom_right().translated(-bottom_right_corner.horizontal_radius + 1, -bottom_right_corner.vertical_radius + 1));
auto bottom_right = bottom_right_corner.as_rect().to_type<DevicePixels>().translated(device_content_rect.bottom_right().translated(-bottom_right_corner.horizontal_radius, -bottom_right_corner.vertical_radius));
paint_shadow(bottom_right);
}
if (bottom_left_corner) {
// Inside the bottom left corner (the part outside the border radius)
auto bottom_left = bottom_left_corner.as_rect().to_type<DevicePixels>().translated(device_content_rect.bottom_left().translated(0, -bottom_left_corner.vertical_radius + 1));
auto bottom_left = bottom_left_corner.as_rect().to_type<DevicePixels>().translated(device_content_rect.bottom_left().translated(0, -bottom_left_corner.vertical_radius));
paint_shadow(bottom_left);
}
}