From 8cd1f655077d6b30bfc46e18b5c371297ca465cc Mon Sep 17 00:00:00 2001 From: Zaggy1024 Date: Sat, 26 Aug 2023 16:59:20 -0500 Subject: [PATCH] LibWeb: Add a CSSPixelFraction class to allow comparison of fractions This class will allow us to compare the ratio of two `CSSPixels` values losslessly. Not only that, but an operation like `a * (b / c)` should no longer be lossy, since the operation can be carried out as `(a * b) / c` implicitly instead. --- Userland/Libraries/LibWeb/PixelUnits.h | 100 ++++++++++++++++++++++--- 1 file changed, 90 insertions(+), 10 deletions(-) diff --git a/Userland/Libraries/LibWeb/PixelUnits.h b/Userland/Libraries/LibWeb/PixelUnits.h index 17f087bfc5..1ac118def8 100644 --- a/Userland/Libraries/LibWeb/PixelUnits.h +++ b/Userland/Libraries/LibWeb/PixelUnits.h @@ -51,6 +51,8 @@ constexpr DevicePixels operator/(DevicePixels left, T right) { return left.value template constexpr DevicePixels operator%(DevicePixels left, T right) { return left.value() % right; } +class CSSPixelFraction; + /// CSSPixels: A position or length in CSS "reference pixels", independent of zoom or screen DPI. /// See https://www.w3.org/TR/css-values-3/#reference-pixel class CSSPixels { @@ -192,16 +194,10 @@ public: return from_raw(int_value); } + constexpr CSSPixels operator*(CSSPixelFraction const& other) const; - constexpr CSSPixels operator/(CSSPixels const& other) const - { - i64 mult = raw_value(); - mult <<= fractional_bits; - mult /= other.raw_value(); - - int int_value = AK::clamp_to_int(mult); - return from_raw(int_value); - } + constexpr CSSPixelFraction operator/(CSSPixels const& other) const; + constexpr CSSPixels operator/(CSSPixelFraction const& other) const; constexpr CSSPixels& operator+=(CSSPixels const& other) { @@ -218,9 +214,14 @@ public: *this = *this * other; return *this; } + constexpr CSSPixels& operator*=(CSSPixelFraction const& other) + { + *this = *this * other; + return *this; + } constexpr CSSPixels& operator/=(CSSPixels const& other) { - *this = *this / other; + *this = *this * other; return *this; } @@ -278,6 +279,85 @@ constexpr CSSPixels operator*(unsigned long left, CSSPixels right) { return righ inline float operator*(float left, CSSPixels right) { return right.to_float() * left; } inline double operator*(double left, CSSPixels right) { return right.to_double() * left; } +class CSSPixelFraction { +public: + constexpr CSSPixelFraction(CSSPixels numerator, CSSPixels denominator) + : m_numerator(numerator) + , m_denominator(denominator) + { + } + + constexpr CSSPixelFraction(CSSPixels value) + : m_numerator(value) + , m_denominator(1) + { + } + + template + constexpr CSSPixelFraction(I numerator, I denominator = 1) + : m_numerator(numerator) + , m_denominator(denominator) + { + } + + constexpr operator CSSPixels() const + { + i64 wide_value = m_numerator.raw_value(); + wide_value <<= CSSPixels::fractional_bits; + wide_value /= m_denominator.raw_value(); + return CSSPixels::from_raw(AK::clamp_to_int(wide_value)); + } + + constexpr int operator<=>(CSSPixelFraction const& other) const + { + auto left = static_cast(m_numerator.raw_value()) * other.m_denominator.raw_value(); + auto right = static_cast(other.m_numerator.raw_value()) * m_denominator.raw_value(); + if (left > right) + return 1; + if (left < right) + return -1; + return 0; + } + + template + constexpr int operator<=>(I const& other) const + { + return *this <=> CSSPixelFraction(other); + } + + constexpr CSSPixels numerator() const { return m_numerator; } + constexpr CSSPixels denominator() const { return m_denominator; } + + float to_float() const { return CSSPixels(*this).to_float(); } + double to_double() const { return CSSPixels(*this).to_double(); } + int to_int() const { return CSSPixels(*this).to_int(); } + bool might_be_saturated() const { return CSSPixels(*this).might_be_saturated(); } + +private: + CSSPixels m_numerator; + CSSPixels m_denominator; +}; + +constexpr CSSPixels CSSPixels::operator*(CSSPixelFraction const& other) const +{ + i64 wide_value = raw_value(); + wide_value *= other.numerator().raw_value(); + wide_value /= other.denominator().raw_value(); + return CSSPixels::from_raw(AK::clamp_to_int(wide_value)); +} + +constexpr CSSPixelFraction CSSPixels::operator/(CSSPixels const& other) const +{ + return CSSPixelFraction(*this, other); +} +constexpr CSSPixels CSSPixels::operator/(CSSPixelFraction const& other) const +{ + i64 wide_value = raw_value(); + wide_value *= other.denominator().raw_value(); + wide_value /= other.numerator().raw_value(); + return CSSPixels::from_raw(AK::clamp_to_int(wide_value)); +} + constexpr CSSPixels operator/(CSSPixels left, int right) { return left / CSSPixels(right); } constexpr CSSPixels operator/(CSSPixels left, unsigned long right) { return left / CSSPixels(right); } inline float operator/(CSSPixels left, float right) { return left.to_float() / right; }