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

LibWeb: Make CSSPixels and Length use 64-bit (double) floating point

This fixes a plethora of rounding problems on many websites.
In the future, we may want to replace this with fixed-point arithmetic
(bug #18566) for performance (and consistency with other engines),
but in the meantime this makes the web look a bit better. :^)

There's a lot more things that could be converted to doubles, which
would reduce the amount of casting necessary in this patch.
We can do that incrementally, however.
This commit is contained in:
Andreas Kling 2023-05-24 10:50:57 +02:00
parent 30262d7023
commit 655d9d1462
80 changed files with 298 additions and 299 deletions

View file

@ -148,16 +148,16 @@ void paint_background(PaintContext& context, Layout::NodeWithStyleAndBoxModelMet
CSSPixelRect image_rect;
switch (layer.size_type) {
case CSS::BackgroundSize::Contain: {
float max_width_ratio = (background_positioning_area.width() / natural_image_width).value();
float max_height_ratio = (background_positioning_area.height() / natural_image_height).value();
float ratio = min(max_width_ratio, max_height_ratio);
double max_width_ratio = (background_positioning_area.width() / natural_image_width).value();
double max_height_ratio = (background_positioning_area.height() / natural_image_height).value();
double ratio = min(max_width_ratio, max_height_ratio);
image_rect.set_size(natural_image_width * ratio, natural_image_height * ratio);
break;
}
case CSS::BackgroundSize::Cover: {
float max_width_ratio = (background_positioning_area.width() / natural_image_width).value();
float max_height_ratio = (background_positioning_area.height() / natural_image_height).value();
float ratio = max(max_width_ratio, max_height_ratio);
double max_width_ratio = (background_positioning_area.width() / natural_image_width).value();
double max_height_ratio = (background_positioning_area.height() / natural_image_height).value();
double ratio = max(max_width_ratio, max_height_ratio);
image_rect.set_size(natural_image_width * ratio, natural_image_height * ratio);
break;
}
@ -253,7 +253,7 @@ void paint_background(PaintContext& context, Layout::NodeWithStyleAndBoxModelMet
repeat_x = false;
} else {
auto space = fmod(background_positioning_area.width(), image_rect.width());
x_step = image_rect.width() + (space / (float)(whole_images - 1));
x_step = image_rect.width() + (space / static_cast<double>(whole_images - 1));
repeat_x = true;
}
break;
@ -284,7 +284,7 @@ void paint_background(PaintContext& context, Layout::NodeWithStyleAndBoxModelMet
repeat_y = false;
} else {
auto space = fmod(background_positioning_area.height(), image_rect.height());
y_step = image_rect.height() + ((float)space / (float)(whole_images - 1));
y_step = image_rect.height() + (static_cast<double>(space) / static_cast<double>(whole_images - 1));
repeat_y = true;
}
break;

View file

@ -33,14 +33,14 @@ BorderRadiiData normalized_border_radii_data(Layout::Node const& node, CSSPixelR
// Scale overlapping curves according to https://www.w3.org/TR/css-backgrounds-3/#corner-overlap
CSSPixels f = 1.0f;
auto width_reciprocal = 1.0f / rect.width().value();
auto height_reciprocal = 1.0f / rect.height().value();
auto width_reciprocal = 1.0 / rect.width().value();
auto height_reciprocal = 1.0 / rect.height().value();
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.value();
f = 1.0 / f.value();
top_left_radius_px.horizontal_radius *= f;
top_left_radius_px.vertical_radius *= f;
@ -149,8 +149,8 @@ void paint_border(PaintContext& context, BorderEdge edge, DevicePixelRect const&
auto draw_border = [&](auto const& border, auto const& radius, auto const& opposite_border, auto const& opposite_radius, auto p1_step_translate, auto p2_step_translate) {
auto [p1, p2] = points_for_edge(edge, rect);
auto p1_step = radius ? 0 : border.width / static_cast<float>(device_pixel_width.value());
auto p2_step = opposite_radius ? 0 : opposite_border.width / static_cast<float>(device_pixel_width.value());
auto p1_step = radius ? 0 : border.width / device_pixel_width.value();
auto p2_step = opposite_radius ? 0 : opposite_border.width / device_pixel_width.value();
for (DevicePixels i = 0; i < device_pixel_width; ++i) {
draw_horizontal_or_vertical_line(p1, p2);
p1_step_translate(p1, p1_step);

View file

@ -111,11 +111,11 @@ static ColorStopData resolve_color_stop_positions(auto const& color_stop_list, a
LinearGradientData resolve_linear_gradient_data(Layout::Node const& node, CSSPixelSize gradient_size, CSS::LinearGradientStyleValue const& linear_gradient)
{
auto gradient_angle = linear_gradient.angle_degrees(gradient_size);
auto gradient_length_px = Gfx::calculate_gradient_length(gradient_size, gradient_angle);
auto gradient_length_px = Gfx::calculate_gradient_length(gradient_size.to_type<double>().to_type<float>(), gradient_angle);
auto resolved_color_stops = resolve_color_stop_positions(
linear_gradient.color_stop_list(), [&](auto const& length_percentage) {
return length_percentage.to_px(node, gradient_length_px).value() / gradient_length_px;
return length_percentage.to_px(node, gradient_length_px).value() / static_cast<double>(gradient_length_px);
},
linear_gradient.is_repeating());

View file

@ -32,10 +32,10 @@ void MarkerPaintable::paint(PaintContext& context, PaintPhase phase) const
return;
// FIXME: All this does is round to the nearest whole CSS pixel, but it's goofy.
CSSPixelRect enclosing = absolute_rect().to_type<float>().to_rounded<float>().to_type<CSSPixels>();
CSSPixelRect enclosing = absolute_rect().to_type<double>().to_type<float>().to_rounded<float>().to_type<CSSPixels>();
auto device_enclosing = context.enclosing_device_rect(enclosing);
CSSPixels marker_width = enclosing.height() / 2.0f;
CSSPixels marker_width = enclosing.height() / 2.0;
if (auto const* list_style_image = layout_box().list_style_image()) {
CSSPixelRect image_rect {

View file

@ -10,7 +10,7 @@
namespace Web {
PaintContext::PaintContext(Gfx::Painter& painter, Palette const& palette, float device_pixels_per_css_pixel)
PaintContext::PaintContext(Gfx::Painter& painter, Palette const& palette, double device_pixels_per_css_pixel)
: m_painter(painter)
, m_palette(palette)
, m_device_pixels_per_css_pixel(device_pixels_per_css_pixel)

View file

@ -18,7 +18,7 @@ namespace Web {
class PaintContext {
public:
PaintContext(Gfx::Painter& painter, Palette const& palette, float device_pixels_per_css_pixel);
PaintContext(Gfx::Painter& painter, Palette const& palette, double device_pixels_per_css_pixel);
Gfx::Painter& painter() const { return m_painter; }
Palette const& palette() const { return m_palette; }
@ -64,13 +64,13 @@ public:
return clone;
}
float device_pixels_per_css_pixel() const { return m_device_pixels_per_css_pixel; }
double device_pixels_per_css_pixel() const { return m_device_pixels_per_css_pixel; }
private:
Gfx::Painter& m_painter;
Palette m_palette;
Optional<SVGContext> m_svg_context;
float m_device_pixels_per_css_pixel;
double m_device_pixels_per_css_pixel { 0 };
DevicePixelRect m_device_viewport_rect;
bool m_should_show_line_box_borders { false };
bool m_focus { false };

View file

@ -155,7 +155,7 @@ void PaintableBox::paint(PaintContext& context, PaintPhase phase) const
if (should_clip_rect) {
context.painter().save();
auto border_box = absolute_border_box_rect();
context.painter().add_clip_rect(context.rounded_device_rect(clip_rect.to_rect().resolved(Paintable::layout_node(), border_box.to_type<float>()).to_type<CSSPixels>()).to_type<int>());
context.painter().add_clip_rect(context.rounded_device_rect(clip_rect.to_rect().resolved(Paintable::layout_node(), border_box.to_type<double>()).to_type<CSSPixels>()).to_type<int>());
}
paint_backdrop_filter(context);
paint_background(context);
@ -434,7 +434,7 @@ static void paint_text_decoration(PaintContext& context, Gfx::Painter& painter,
CSSPixels css_line_thickness = [&] {
CSS::Length computed_thickness = text_node.computed_values().text_decoration_thickness().resolved(text_node, CSS::Length(1, CSS::Length::Type::Em));
if (computed_thickness.is_auto())
return max(glyph_height * 0.1f, 1.f);
return max(glyph_height * 0.1, 1.);
return computed_thickness.to_px(text_node);
}();

View file

@ -36,7 +36,7 @@ Optional<HitTestResult> SVGGeometryPaintable::hit_test(CSSPixelPoint position, H
if (auto transform = layout_box().layout_transform(); transform.has_value()) {
auto transformed_bounding_box = transform->map_to_quad(
const_cast<SVG::SVGGeometryElement&>(geometry_element).get_path().bounding_box());
if (!transformed_bounding_box.contains(position.to_type<float>()))
if (!transformed_bounding_box.contains(position.to_type<double>().to_type<float>()))
return {};
}
return result;
@ -92,7 +92,7 @@ void SVGGeometryPaintable::paint(PaintContext& context, PaintPhase phase) const
auto svg_viewport = [&] {
if (maybe_view_box.has_value())
return Gfx::FloatRect { maybe_view_box->min_x, maybe_view_box->min_y, maybe_view_box->width, maybe_view_box->height };
return Gfx::FloatRect { { 0, 0 }, svg_context.svg_element_size().to_type<float>() };
return Gfx::FloatRect { { 0, 0 }, svg_context.svg_element_size().to_type<double>().to_type<float>() };
}();
SVG::SVGPaintContext paint_context {

View file

@ -232,7 +232,7 @@ Gfx::FloatMatrix4x4 StackingContext::get_transformation_matrix(CSS::Transformati
auto count = transformation.values.size();
auto value = [this, transformation](size_t index, Optional<CSS::Length const&> reference_length = {}) -> float {
return transformation.values[index].visit(
[this, reference_length](CSS::LengthPercentage const& value) {
[this, reference_length](CSS::LengthPercentage const& value) -> float {
if (reference_length.has_value()) {
return value.resolved(m_box, reference_length.value()).to_px(m_box).value();
}
@ -430,7 +430,7 @@ Gfx::FloatPoint StackingContext::compute_transform_origin() const
auto reference_box = paintable_box().absolute_border_box_rect();
auto x = reference_box.left() + style_value.x.to_px(m_box, reference_box.width());
auto y = reference_box.top() + style_value.y.to_px(m_box, reference_box.height());
return { x, y };
return { static_cast<float>(x.value()), static_cast<float>(y.value()) };
}
template<typename U, typename Callback>

View file

@ -378,7 +378,7 @@ VideoPaintable::DispatchEventOfSameName VideoPaintable::handle_mouseup(Badge<Eve
if (cached_layout_boxes.timeline_rect.has_value() && cached_layout_boxes.timeline_rect->contains(position)) {
auto x_offset = position.x() - cached_layout_boxes.timeline_rect->x();
auto x_percentage = static_cast<float>(x_offset) / static_cast<float>(cached_layout_boxes.timeline_rect->width());
auto x_percentage = static_cast<double>(x_offset) / static_cast<double>(cached_layout_boxes.timeline_rect->width());
auto position = static_cast<double>(x_percentage) * video_element.duration();
video_element.set_current_time(position);