diff --git a/Tests/LibWeb/TestCSSPixels.cpp b/Tests/LibWeb/TestCSSPixels.cpp index 99c839a7ba..2948ef7231 100644 --- a/Tests/LibWeb/TestCSSPixels.cpp +++ b/Tests/LibWeb/TestCSSPixels.cpp @@ -39,6 +39,17 @@ TEST_CASE(multiplication1) CSSPixels b(4); CSSPixels c = a * b; EXPECT_EQ(c, CSSPixels(12)); + + // Temporary overflow + a = CSSPixels::from_raw(0xFFFF'FFFF >> (CSSPixels::fractional_bits + 1)); + b = 1; + EXPECT_EQ((a * b), a); + + // Rounding + a = CSSPixels::from_raw(0b01'000001); + b = CSSPixels::from_raw(0b01'100000); + EXPECT_EQ(a * b, CSSPixels(a.to_double() * b.to_double())); + EXPECT_EQ(a * -b, CSSPixels(a.to_double() * -b.to_double())); } TEST_CASE(addition2) diff --git a/Userland/Libraries/LibWeb/PixelUnits.cpp b/Userland/Libraries/LibWeb/PixelUnits.cpp index 8edc6e6bec..5b1d6c0cb8 100644 --- a/Userland/Libraries/LibWeb/PixelUnits.cpp +++ b/Userland/Libraries/LibWeb/PixelUnits.cpp @@ -9,9 +9,6 @@ namespace Web { -static i32 const fractional_bits = 6; -static constexpr i32 fixed_point_denominator = 1 << fractional_bits; - CSSPixels::CSSPixels(int value) { m_value = value * fixed_point_denominator; @@ -103,9 +100,26 @@ CSSPixels CSSPixels::operator-(CSSPixels const& other) const CSSPixels CSSPixels::operator*(CSSPixels const& other) const { - CSSPixels result; - result.set_raw_value((static_cast(raw_value()) * other.raw_value()) >> fractional_bits); - return result; + i64 value = raw_value(); + value *= other.raw_value(); + + int int_value = AK::clamp_to_int(value >> fractional_bits); + + // Rounding: + // If last bit cut off was 1: + if (value & (1u << (fractional_bits - 1))) { + // If the bit after was 1 as well + if (value & (radix_mask >> 2u)) { + // We need to round away from 0 + int_value = Checked::saturating_add(int_value, 1); + } else { + // Otherwise we round to the next even value + // Which means we add the least significant bit of the raw integer value + int_value = Checked::saturating_add(int_value, int_value & 1); + } + } + + return from_raw(int_value); } CSSPixels CSSPixels::operator/(CSSPixels const& other) const diff --git a/Userland/Libraries/LibWeb/PixelUnits.h b/Userland/Libraries/LibWeb/PixelUnits.h index f83333828f..e1dd8a1a42 100644 --- a/Userland/Libraries/LibWeb/PixelUnits.h +++ b/Userland/Libraries/LibWeb/PixelUnits.h @@ -52,6 +52,11 @@ constexpr DevicePixels operator%(DevicePixels left, T right) { return left.value /// See https://www.w3.org/TR/css-values-3/#reference-pixel class CSSPixels { public: + static constexpr i32 fractional_bits = 6; + static constexpr i32 fixed_point_denominator = 1 << fractional_bits; + + static constexpr i32 radix_mask = fixed_point_denominator - 1; + CSSPixels() = default; CSSPixels(int value); CSSPixels(unsigned int value);