Преглед на файлове

LibWeb: Add propper rounding to PixelUnits::operator*

Also moves related constants into the class to make them accessible for
tests.
Hendiadyoin1 преди 1 година
родител
ревизия
f9fc0505fb
променени са 3 файла, в които са добавени 36 реда и са изтрити 6 реда
  1. 11 0
      Tests/LibWeb/TestCSSPixels.cpp
  2. 20 6
      Userland/Libraries/LibWeb/PixelUnits.cpp
  3. 5 0
      Userland/Libraries/LibWeb/PixelUnits.h

+ 11 - 0
Tests/LibWeb/TestCSSPixels.cpp

@@ -39,6 +39,17 @@ TEST_CASE(multiplication1)
     CSSPixels b(4);
     CSSPixels b(4);
     CSSPixels c = a * b;
     CSSPixels c = a * b;
     EXPECT_EQ(c, CSSPixels(12));
     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)
 TEST_CASE(addition2)

+ 20 - 6
Userland/Libraries/LibWeb/PixelUnits.cpp

@@ -9,9 +9,6 @@
 
 
 namespace Web {
 namespace Web {
 
 
-static i32 const fractional_bits = 6;
-static constexpr i32 fixed_point_denominator = 1 << fractional_bits;
-
 CSSPixels::CSSPixels(int value)
 CSSPixels::CSSPixels(int value)
 {
 {
     m_value = value * fixed_point_denominator;
     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 CSSPixels::operator*(CSSPixels const& other) const
 {
 {
-    CSSPixels result;
-    result.set_raw_value((static_cast<i64>(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<int>::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<int>::saturating_add(int_value, int_value & 1);
+        }
+    }
+
+    return from_raw(int_value);
 }
 }
 
 
 CSSPixels CSSPixels::operator/(CSSPixels const& other) const
 CSSPixels CSSPixels::operator/(CSSPixels const& other) const

+ 5 - 0
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
 /// See https://www.w3.org/TR/css-values-3/#reference-pixel
 class CSSPixels {
 class CSSPixels {
 public:
 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() = default;
     CSSPixels(int value);
     CSSPixels(int value);
     CSSPixels(unsigned int value);
     CSSPixels(unsigned int value);