blob: 2a22c93cbf46c15786ad3efcb896d074264cc6c4 [file] [log] [blame]
/*
* Copyright (c) 2012, Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_LAYOUT_UNIT_H_
#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_LAYOUT_UNIT_H_
#include <climits>
#include <iosfwd>
#include <limits>
#include <optional>
#include <type_traits>
#include "base/check_op.h"
#include "base/compiler_specific.h"
#include "base/dcheck_is_on.h"
#include "base/logging.h"
#include "base/numerics/clamped_math.h"
#include "base/numerics/safe_conversions.h"
#include "build/build_config.h"
#include "third_party/blink/renderer/platform/platform_export.h"
#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
#include "third_party/blink/renderer/platform/wtf/forward.h"
#include "third_party/blink/renderer/platform/wtf/vector_traits.h"
namespace blink {
#if DCHECK_IS_ON()
#define REPORT_OVERFLOW(doesOverflow) \
DLOG_IF(ERROR, !(doesOverflow)) << "LayoutUnit overflow !(" << #doesOverflow \
<< ") in " << PRETTY_FUNCTION
#else
#define REPORT_OVERFLOW(doesOverflow) ((void)0)
#endif
//
// `FixedPoint` is a fixed-point math class template, with the number of bits
// for the fractional part as a template parameter.
//
// `LayoutUnit` is an instantiated class of the `FixedPoint`, storing multiples
// of 1/64 of a pixel in an `int32_t` storage.
// See: https://trac.webkit.org/wiki/LayoutUnit
//
// `TextRunLayoutUnit` stores multiples of 1/65536 of a pixel in the same
// storage, so it has less integral part than `LayoutUnit` (16/16 bits vs 26/6
// bits). Suitable for a run of text, but not for the whole layout space.
//
// `InlineLayoutUnit` stores the same precision as `TextRunLayoutUnit` using an
// `int64_t` storage. It can provide the text precision, and represent the whole
// layout space (and more, 48 bits vs 26 bits), but it's double-sized.
//
// Note, non-member functions and operators for `TextRunLayoutUnit` and
// `InlineLayoutUnit` are implemented as needed.
//
template <unsigned fractional_bits, typename Storage>
class PLATFORM_EXPORT FixedPoint {
DISALLOW_NEW();
public:
using StorageType = Storage;
using UnsignedStorageType = std::make_unsigned<Storage>::type;
static constexpr unsigned kFractionalBits = fractional_bits;
static constexpr unsigned kIntegralBits =
sizeof(Storage) * 8 - kFractionalBits;
static constexpr int kFixedPointDenominator = 1 << kFractionalBits;
static constexpr Storage kRawValueMax = std::numeric_limits<Storage>::max();
static constexpr Storage kRawValueMin = std::numeric_limits<Storage>::min();
static constexpr Storage kIntMax = kRawValueMax / kFixedPointDenominator;
static constexpr Storage kIntMin = kRawValueMin / kFixedPointDenominator;
template <typename T>
static constexpr Storage ClampRawValue(T raw_value) {
return base::saturated_cast<Storage>(raw_value);
}
constexpr FixedPoint() : value_(0) {}
// Creates a `FixedPoint` with the specified integer value.
// If the specified value is smaller than `FixedPoint::Min()`, the new
// `FixedPoint` is equivalent to `FixedPoint::Min()`.
// If the specified value is greater than the maximum integer value which
// `FixedPoint` can represent, the new `FixedPoint` is equivalent to
// `FixedPoint(FixedPoint::kIntMax)` in 32-bit Arm, or is equivalent to
// `FixedPoint::Max()` otherwise.
constexpr explicit FixedPoint(std::signed_integral auto value)
requires(sizeof(value) <= sizeof(int))
: value_(0) {
SaturatedSet(static_cast<int>(value));
}
constexpr explicit FixedPoint(std::unsigned_integral auto value)
requires(sizeof(value) <= sizeof(int))
: value_(0) {
SaturatedSet(static_cast<unsigned>(value));
}
constexpr explicit FixedPoint(std::integral auto value)
requires(sizeof(value) > sizeof(int))
: value_(ClampRawValue(value * kFixedPointDenominator)) {}
// The specified `value` is truncated to a multiple of `Epsilon()` near 0, and
// is clamped by `Min()` and `Max()`. A NaN `value` produces `FixedPoint(0)`.
constexpr explicit FixedPoint(float value)
: value_(ClampRawValue(value * kFixedPointDenominator)) {}
constexpr explicit FixedPoint(double value)
: value_(ClampRawValue(value * kFixedPointDenominator)) {}
// The specified `value` is rounded up to a multiple of `Epsilon()`, and is
// clamped by `Min()` and `Max()`. A NaN `value` produces `FixedPoint(0)`.
static FixedPoint FromFloatCeil(float value) {
return FromRawValueWithClamp(ceilf(value * kFixedPointDenominator));
}
// The specified `value` is truncated to a multiple of `Epsilon()`, and is
// clamped by `Min()` and `Max()`. A NaN `value` produces `FixedPoint(0)`.
static FixedPoint FromFloatFloor(float value) {
return FromRawValueWithClamp(floorf(value * kFixedPointDenominator));
}
// The specified `value` is rounded to a multiple of `Epsilon()`, and is
// clamped by `Min()` and `Max()`. A NaN `value` produces `FixedPoint(0)`.
static FixedPoint FromFloatRound(float value) {
return FromRawValueWithClamp(roundf(value * kFixedPointDenominator));
}
static FixedPoint FromDoubleRound(double value) {
return FromRawValueWithClamp(round(value * kFixedPointDenominator));
}
static constexpr FixedPoint FromRawValue(Storage raw_value) {
FixedPoint v;
v.value_ = raw_value;
return v;
}
template <typename T>
static constexpr FixedPoint FromRawValueWithClamp(T raw_value) {
return FromRawValue(ClampRawValue(raw_value));
}
// Construct from a `FixedPoint` with different template parameters. Implicit
// because it's lossless. For lossy conversions, use `To<>()` below instead.
template <unsigned source_fractional_bits, typename SourceStorage>
requires(
sizeof(Storage) > sizeof(SourceStorage) &&
kFractionalBits >= source_fractional_bits &&
kIntegralBits >=
FixedPoint<source_fractional_bits, SourceStorage>::kIntegralBits)
FixedPoint(FixedPoint<source_fractional_bits, SourceStorage> source)
: value_(static_cast<Storage>(source.RawValue())
<< (kFractionalBits - source_fractional_bits)) {}
// Convert from a fixed point integer.
template <unsigned source_fractional_bits>
requires(source_fractional_bits == kFractionalBits)
static constexpr FixedPoint FromFixed(Storage value) {
return FromRawValue(value);
}
template <unsigned source_fractional_bits>
requires(source_fractional_bits >= kFractionalBits)
static constexpr FixedPoint FromFixed(std::integral auto value) {
constexpr unsigned kBitsDiff = source_fractional_bits - kFractionalBits;
return FromRawValueWithClamp(value >> kBitsDiff);
}
template <unsigned source_fractional_bits>
requires(kFractionalBits > source_fractional_bits)
static constexpr FixedPoint FromFixed(std::integral auto value) {
constexpr unsigned kBitsDiff = kFractionalBits - source_fractional_bits;
if (value >= kRawValueMax >> kBitsDiff) [[unlikely]] {
return Max();
}
if (value <= kRawValueMin >> kBitsDiff) [[unlikely]] {
return Min();
}
return FromRawValue(value << kBitsDiff);
}
// Convert to a `FixedPoint` with a different storage and/or precision.
template <typename Target>
constexpr Target To() const {
return Target::template FromFixed<kFractionalBits>(RawValue());
}
// Convert to a different `FixedPoint` by ceiling the lost precisions (e.g.,
// `InlineLayoutUnit` to `LayoutUnit`).
template <typename Target>
requires(Target::kFractionalBits < kFractionalBits &&
sizeof(typename Target::StorageType) < sizeof(Storage))
constexpr Target ToCeil() const {
constexpr unsigned kBitsDiff = kFractionalBits - Target::kFractionalBits;
Storage raw_value = RawValue() >> kBitsDiff;
if (RawValue() & ((1 << kBitsDiff) - 1)) {
++raw_value;
}
return Target::FromRawValueWithClamp(raw_value);
}
constexpr Storage ToInt() const { return value_ / kFixedPointDenominator; }
constexpr float ToFloat() const {
return static_cast<float>(value_) / kFixedPointDenominator;
}
constexpr double ToDouble() const {
return static_cast<double>(value_) / kFixedPointDenominator;
}
UnsignedStorageType ToUnsigned() const {
REPORT_OVERFLOW(value_ >= 0);
return ToInt();
}
// Conversion to int or unsigned is lossy. 'explicit' on these operators won't
// work because there are also other implicit conversion paths (e.g. operator
// bool then to int which would generate wrong result). Use toInt() and
// toUnsigned() instead.
operator int() const = delete;
operator unsigned() const = delete;
constexpr operator double() const { return ToDouble(); }
constexpr operator float() const { return ToFloat(); }
constexpr operator bool() const { return value_; }
std::strong_ordering operator<=>(const FixedPoint&) const = default;
std::partial_ordering operator<=>(double d) const { return ToDouble() <=> d; }
std::partial_ordering operator<=>(float f) const { return ToFloat() <=> f; }
FixedPoint operator++(int) {
value_ = base::ClampAdd(value_, kFixedPointDenominator);
return *this;
}
constexpr Storage RawValue() const { return value_; }
inline void SetRawValue(int value) { value_ = value; }
void SetRawValue(int64_t value) {
if constexpr (sizeof(Storage) < sizeof(int64_t)) {
REPORT_OVERFLOW(value > kRawValueMin && value < kRawValueMax);
}
value_ = static_cast<Storage>(value);
}
FixedPoint Abs() const { return FromRawValue(::abs(value_)); }
Storage Ceil() const {
if (value_ >= kRawValueMax - kFixedPointDenominator + 1) [[unlikely]] {
return kIntMax;
}
if (value_ >= 0)
return (value_ + kFixedPointDenominator - 1) / kFixedPointDenominator;
return ToInt();
}
ALWAYS_INLINE Storage Round() const {
return ToInt() + ((Fraction().RawValue() + (kFixedPointDenominator / 2)) >>
kFractionalBits);
}
Storage Floor() const {
if (value_ <= kRawValueMin + kFixedPointDenominator - 1) [[unlikely]] {
return kIntMin;
}
return value_ >> kFractionalBits;
}
FixedPoint ClampNegativeToZero() const {
return value_ < 0 ? FixedPoint() : *this;
}
FixedPoint ClampPositiveToZero() const {
return value_ > 0 ? FixedPoint() : *this;
}
FixedPoint ClampIndefiniteToZero() const {
// We compare to |kFixedPointDenominator| here instead of |kIndefiniteSize|
// as the operator== for LayoutUnit is inlined below.
if (value_ == -kFixedPointDenominator)
return FixedPoint();
DCHECK_GE(value_, 0);
return *this;
}
constexpr bool HasFraction() const {
return RawValue() % kFixedPointDenominator;
}
constexpr bool IsInteger() const { return !HasFraction(); }
FixedPoint Fraction() const {
// Compute fraction using the mod operator to preserve the sign of the value
// as it may affect rounding.
return FromRawValue(RawValue() % kFixedPointDenominator);
}
bool MightBeSaturated() const {
return RawValue() == kRawValueMax || RawValue() == kRawValueMin;
}
static constexpr float Epsilon() { return 1.0f / kFixedPointDenominator; }
FixedPoint AddEpsilon() const {
return FromRawValue(value_ < kRawValueMax ? value_ + 1 : value_);
}
static constexpr FixedPoint Max() { return FromRawValue(kRawValueMax); }
static constexpr FixedPoint Min() { return FromRawValue(kRawValueMin); }
// Versions of max/min that are slightly smaller/larger than max/min() to
// allow for rounding without overflowing.
static constexpr FixedPoint NearlyMax() {
return FromRawValue(kRawValueMax - kFixedPointDenominator / 2);
}
static constexpr FixedPoint NearlyMin() {
return FromRawValue(kRawValueMin + kFixedPointDenominator / 2);
}
static FixedPoint Clamp(double value) { return FromFloatFloor(value); }
// Multiply by |m| and divide by |d| as a single ("fused") operation, avoiding
// any saturation of the intermediate result. Rounding matches that of the
// regular operations (i.e the result of the divide is rounded towards zero).
FixedPoint MulDiv(FixedPoint m, FixedPoint d) const;
// Return `std::nullopt` if `this` is the specified value.
std::optional<FixedPoint> NullOptIf(FixedPoint null_value) const;
std::optional<FixedPoint> NullOptIfMin() const { return NullOptIf(Min()); }
String ToString() const;
private:
#if defined(ARCH_CPU_ARM_FAMILY) && defined(ARCH_CPU_32_BITS) && \
defined(COMPILER_GCC) && __OPTIMIZE__
// If we're building ARM 32-bit on GCC we replace the C++ versions with some
// native ARM assembly for speed.
constexpr inline void SaturatedSet(int value) {
if (std::is_constant_evaluated() || sizeof(Storage) > sizeof(int)) {
SaturatedSetNonAsm(value);
} else {
SaturatedSetAsm(value);
}
}
inline void SaturatedSetAsm(int value) {
// Figure out how many bits are left for storing the integer part of
// the fixed point number, and saturate our input to that
enum { Saturate = 32 - kFractionalBits };
int result;
// The following ARM code will Saturate the passed value to the number of
// bits used for the whole part of the fixed point representation, then
// shift it up into place. This will result in the low
// <kFractionalBits> bits all being 0's. When the value saturates
// this gives a different result to from the C++ case; in the C++ code a
// saturated value has all the low bits set to 1 (for a +ve number at
// least). This cannot be done rapidly in ARM ... we live with the
// difference, for the sake of speed.
asm("ssat %[output],%[saturate],%[value]\n\t"
"lsl %[output],%[shift]"
: [output] "=r"(result)
: [value] "r"(value), [saturate] "n"(Saturate),
[shift] "n"(kFractionalBits));
value_ = result;
}
constexpr inline void SaturatedSet(unsigned value) {
if (std::is_constant_evaluated() || sizeof(Storage) > sizeof(int)) {
SaturatedSetNonAsm(value);
} else {
SaturatedSetAsm(value);
}
}
inline void SaturatedSetAsm(unsigned value) {
// Here we are being passed an unsigned value to saturate,
// even though the result is returned as a signed integer. The ARM
// instruction for unsigned saturation therefore needs to be given one
// less bit (i.e. the sign bit) for the saturation to work correctly; hence
// the '31' below.
enum { Saturate = 31 - kFractionalBits };
// The following ARM code will Saturate the passed value to the number of
// bits used for the whole part of the fixed point representation, then
// shift it up into place. This will result in the low
// <kFractionalBits> bits all being 0's. When the value saturates
// this gives a different result to from the C++ case; in the C++ code a
// saturated value has all the low bits set to 1. This cannot be done
// rapidly in ARM, so we live with the difference, for the sake of speed.
int result;
asm("usat %[output],%[saturate],%[value]\n\t"
"lsl %[output],%[shift]"
: [output] "=r"(result)
: [value] "r"(value), [saturate] "n"(Saturate),
[shift] "n"(kFractionalBits));
value_ = result;
}
#else // end of 32-bit ARM GCC
ALWAYS_INLINE constexpr void SaturatedSet(int value) {
SaturatedSetNonAsm(value);
}
ALWAYS_INLINE constexpr void SaturatedSet(unsigned value) {
SaturatedSetNonAsm(value);
}
#endif
ALWAYS_INLINE constexpr void SaturatedSetNonAsm(int value) {
if (value > kIntMax) {
value_ = kRawValueMax;
} else if (value < kIntMin) {
value_ = kRawValueMin;
} else {
value_ = static_cast<UnsignedStorageType>(value) << kFractionalBits;
}
}
ALWAYS_INLINE constexpr void SaturatedSetNonAsm(unsigned value) {
if (value >= static_cast<UnsignedStorageType>(kIntMax)) {
value_ = kRawValueMax;
} else {
value_ = static_cast<UnsignedStorageType>(value) << kFractionalBits;
}
}
Storage value_;
};
using LayoutUnit = FixedPoint<6, int32_t>;
using TextRunLayoutUnit = FixedPoint<16, int32_t>;
using InlineLayoutUnit = FixedPoint<16, int64_t>;
// kIndefiniteSize is a special value used within layout code. It is typical
// within layout to have sizes which are only allowed to be non-negative or
// "indefinite". We use the value of "-1" to represent these indefinite values.
//
// It is common to clamp these indefinite values to zero.
// |LayoutUnit::ClampIndefiniteToZero| provides this functionality, and
// additionally DCHECKs that it isn't some other negative value.
inline constexpr LayoutUnit kIndefiniteSize(-1);
// TODO(kojii): Using three-way comparison (spaceship) operator for `int` makes
// too many cases ambiguous.
inline bool operator<=(const LayoutUnit& a, int b) {
return a <= LayoutUnit(b);
}
inline bool operator<=(const int a, const LayoutUnit& b) {
return LayoutUnit(a) <= b;
}
inline bool operator>=(const LayoutUnit& a, int b) {
return a >= LayoutUnit(b);
}
inline bool operator>=(const int a, const LayoutUnit& b) {
return LayoutUnit(a) >= b;
}
inline bool operator<(const LayoutUnit& a, int b) {
return a < LayoutUnit(b);
}
inline bool operator<(const int a, const LayoutUnit& b) {
return LayoutUnit(a) < b;
}
inline bool operator>(const LayoutUnit& a, int b) {
return a > LayoutUnit(b);
}
inline bool operator>(const int a, const LayoutUnit& b) {
return LayoutUnit(a) > b;
}
inline bool operator==(const LayoutUnit& a, int b) {
return a == LayoutUnit(b);
}
inline bool operator==(const int a, const LayoutUnit& b) {
return LayoutUnit(a) == b;
}
// For multiplication that's prone to overflow, this bounds it to
// `FixedPoint::Max()` and `FixedPoint::Min()`.
template <unsigned fractional_bits, typename RawValue>
requires(std::is_same_v<RawValue, int32_t>)
inline FixedPoint<fractional_bits, RawValue> BoundedMultiply(
const FixedPoint<fractional_bits, RawValue>& a,
const FixedPoint<fractional_bits, RawValue>& b) {
int64_t result =
static_cast<int64_t>(a.RawValue()) * static_cast<int64_t>(b.RawValue()) /
FixedPoint<fractional_bits, RawValue>::kFixedPointDenominator;
int32_t high = static_cast<int32_t>(result >> 32);
int32_t low = static_cast<int32_t>(result);
uint32_t saturated =
(static_cast<uint32_t>(a.RawValue() ^ b.RawValue()) >> 31) +
FixedPoint<fractional_bits, RawValue>::kRawValueMax;
// If the higher 32 bits does not match the lower 32 with sign extension the
// operation overflowed.
if (high != low >> 31)
result = saturated;
return FixedPoint<fractional_bits, RawValue>::FromRawValue(
static_cast<RawValue>(result));
}
template <unsigned fractional_bits, typename RawValue>
requires(std::is_same_v<RawValue, int32_t>)
inline FixedPoint<fractional_bits, RawValue> operator*(
const FixedPoint<fractional_bits, RawValue>& a,
const FixedPoint<fractional_bits, RawValue>& b) {
return BoundedMultiply(a, b);
}
inline double operator*(const LayoutUnit& a, double b) {
return a.ToDouble() * b;
}
inline float operator*(const LayoutUnit& a, float b) {
return a.ToFloat() * b;
}
template <unsigned fractional_bits, typename RawValue>
inline FixedPoint<fractional_bits, RawValue> operator*(
const FixedPoint<fractional_bits, RawValue> a,
std::integral auto b) {
return FixedPoint<fractional_bits, RawValue>::FromRawValue(
base::ClampMul(a.RawValue(), b));
}
inline LayoutUnit operator*(std::integral auto a, const LayoutUnit& b) {
return b * a;
}
constexpr float operator*(const float a, const LayoutUnit& b) {
return a * b.ToFloat();
}
constexpr double operator*(const double a, const LayoutUnit& b) {
return a * b.ToDouble();
}
template <unsigned fractional_bits, typename RawValue>
requires(std::is_same_v<RawValue, int32_t>)
inline FixedPoint<fractional_bits, RawValue> operator/(
const FixedPoint<fractional_bits, RawValue>& a,
const FixedPoint<fractional_bits, RawValue>& b) {
int64_t raw_val =
static_cast<int64_t>(
FixedPoint<fractional_bits, RawValue>::kFixedPointDenominator) *
a.RawValue() / b.RawValue();
return FixedPoint<fractional_bits, RawValue>::FromRawValueWithClamp(raw_val);
}
template <unsigned fractional_bits, typename RawValue>
inline FixedPoint<fractional_bits, RawValue>
FixedPoint<fractional_bits, RawValue>::MulDiv(FixedPoint m,
FixedPoint d) const {
int64_t n = static_cast<int64_t>(RawValue()) * m.RawValue();
int64_t q = n / d.RawValue();
return FromRawValueWithClamp(q);
}
constexpr float operator/(const LayoutUnit& a, float b) {
return a.ToFloat() / b;
}
constexpr double operator/(const LayoutUnit& a, double b) {
return a.ToDouble() / b;
}
template <unsigned fractional_bits, typename RawValue>
inline FixedPoint<fractional_bits, RawValue> operator/(
const FixedPoint<fractional_bits, RawValue>& a,
std::integral auto b) {
return FixedPoint<fractional_bits, RawValue>::FromRawValue(a.RawValue() / b);
}
constexpr float operator/(const float a, const LayoutUnit& b) {
return a / b.ToFloat();
}
constexpr double operator/(const double a, const LayoutUnit& b) {
return a / b.ToDouble();
}
inline LayoutUnit operator/(std::integral auto a, const LayoutUnit& b) {
return LayoutUnit(a) / b;
}
template <unsigned fractional_bits, typename RawValue>
requires(std::is_same_v<RawValue, int32_t>)
inline FixedPoint<fractional_bits, RawValue> operator%(
const FixedPoint<fractional_bits, RawValue>& a,
const FixedPoint<fractional_bits, RawValue>& b) {
int64_t raw_val = a.RawValue() % b.RawValue();
return FixedPoint<fractional_bits, RawValue>::FromRawValueWithClamp(raw_val);
}
template <unsigned fractional_bits, typename RawValue>
ALWAYS_INLINE FixedPoint<fractional_bits, RawValue> operator+(
const FixedPoint<fractional_bits, RawValue>& a,
const FixedPoint<fractional_bits, RawValue>& b) {
return FixedPoint<fractional_bits, RawValue>::FromRawValue(
base::ClampAdd(a.RawValue(), b.RawValue()).RawValue());
}
inline LayoutUnit operator+(const LayoutUnit& a, std::integral auto b) {
return a + LayoutUnit(b);
}
template <unsigned fractional_bits, typename RawValue>
inline float operator+(const FixedPoint<fractional_bits, RawValue>& a,
float b) {
return a.ToFloat() + b;
}
inline double operator+(const LayoutUnit& a, double b) {
return a.ToDouble() + b;
}
inline LayoutUnit operator+(std::integral auto a, const LayoutUnit& b) {
return LayoutUnit(a) + b;
}
constexpr float operator+(const float a, const LayoutUnit& b) {
return a + b.ToFloat();
}
constexpr double operator+(const double a, const LayoutUnit& b) {
return a + b.ToDouble();
}
ALWAYS_INLINE LayoutUnit operator-(const LayoutUnit& a, const LayoutUnit& b) {
return LayoutUnit::FromRawValue(
base::ClampSub(a.RawValue(), b.RawValue()).RawValue());
}
inline LayoutUnit operator-(const LayoutUnit& a, std::integral auto b) {
return a - LayoutUnit(b);
}
constexpr float operator-(const LayoutUnit& a, float b) {
return a.ToFloat() - b;
}
constexpr double operator-(const LayoutUnit& a, double b) {
return a.ToDouble() - b;
}
inline LayoutUnit operator-(std::integral auto a, const LayoutUnit& b) {
return LayoutUnit(a) - b;
}
constexpr float operator-(const float a, const LayoutUnit& b) {
return a - b.ToFloat();
}
template <unsigned fractional_bits, typename RawValue>
inline FixedPoint<fractional_bits, RawValue> operator-(
const FixedPoint<fractional_bits, RawValue>& a) {
return FixedPoint<fractional_bits, RawValue>::FromRawValue(
(-base::MakeClampedNum(a.RawValue())).RawValue());
}
// Returns the remainder after a division with integer results.
// This calculates the modulo so that:
// a = static_cast<int>(a / b) * b + IntMod(a, b).
inline LayoutUnit IntMod(const LayoutUnit& a, const LayoutUnit& b) {
return LayoutUnit::FromRawValue(a.RawValue() % b.RawValue());
}
template <unsigned fractional_bits, typename RawValue, typename SourceStorage>
requires(sizeof(SourceStorage) <= sizeof(RawValue))
inline FixedPoint<fractional_bits, RawValue>& operator+=(
FixedPoint<fractional_bits, RawValue>& a,
const FixedPoint<fractional_bits, SourceStorage>& b) {
a.SetRawValue(base::ClampAdd(a.RawValue(), b.RawValue()).RawValue());
return a;
}
template <unsigned fractional_bits, typename RawValue, typename SourceStorage>
requires(sizeof(SourceStorage) <= sizeof(RawValue))
inline FixedPoint<fractional_bits, RawValue> operator+(
const FixedPoint<fractional_bits, RawValue>& a,
const FixedPoint<fractional_bits, SourceStorage>& b) {
FixedPoint<fractional_bits, RawValue> r = a;
r += b;
return r;
}
inline LayoutUnit& operator+=(LayoutUnit& a, std::integral auto b) {
a = a + LayoutUnit(b);
return a;
}
inline LayoutUnit& operator+=(LayoutUnit& a, float b) {
a = LayoutUnit(a + b);
return a;
}
inline float& operator+=(float& a, const LayoutUnit& b) {
a = a + b;
return a;
}
inline LayoutUnit& operator-=(LayoutUnit& a, std::integral auto b) {
a = a - LayoutUnit(b);
return a;
}
template <unsigned fractional_bits, typename RawValue>
inline FixedPoint<fractional_bits, RawValue>& operator-=(
FixedPoint<fractional_bits, RawValue>& a,
const FixedPoint<fractional_bits, RawValue>& b) {
a.SetRawValue(base::ClampSub(a.RawValue(), b.RawValue()).RawValue());
return a;
}
inline LayoutUnit& operator-=(LayoutUnit& a, float b) {
a = LayoutUnit(a - b);
return a;
}
inline float& operator-=(float& a, const LayoutUnit& b) {
a = a - b;
return a;
}
inline LayoutUnit& operator*=(LayoutUnit& a, const LayoutUnit& b) {
a = a * b;
return a;
}
inline LayoutUnit& operator*=(LayoutUnit& a, float b) {
a = LayoutUnit(a * b);
return a;
}
inline float& operator*=(float& a, const LayoutUnit& b) {
a = a * b;
return a;
}
inline LayoutUnit& operator/=(LayoutUnit& a, const LayoutUnit& b) {
a = a / b;
return a;
}
inline LayoutUnit& operator/=(LayoutUnit& a, float b) {
a = LayoutUnit(a / b);
return a;
}
inline float& operator/=(float& a, const LayoutUnit& b) {
a = a / b;
return a;
}
inline int SnapSizeToPixel(LayoutUnit size, LayoutUnit location) {
LayoutUnit fraction = location.Fraction();
int result = (fraction + size).Round() - fraction.Round();
if (result == 0 && (size.RawValue() > 4 || size.RawValue() < -4))
[[unlikely]] {
return size > 0 ? 1 : -1;
}
return result;
}
inline int SnapSizeToPixelAllowingZero(LayoutUnit size, LayoutUnit location) {
LayoutUnit fraction = location.Fraction();
return (fraction + size).Round() - fraction.Round();
}
inline int RoundToInt(LayoutUnit value) {
return value.Round();
}
inline int FloorToInt(LayoutUnit value) {
return value.Floor();
}
inline int CeilToInt(LayoutUnit value) {
return value.Ceil();
}
inline LayoutUnit AbsoluteValue(const LayoutUnit& value) {
return value.Abs();
}
template <unsigned fractional_bits, typename RawValue>
inline std::optional<FixedPoint<fractional_bits, RawValue>>
FixedPoint<fractional_bits, RawValue>::NullOptIf(FixedPoint null_value) const {
if (*this == null_value) {
return std::nullopt;
}
return *this;
}
#if defined(ARCH_CPU_ARM_FAMILY) && defined(ARCH_CPU_32_BITS) && \
defined(COMPILER_GCC) && __OPTIMIZE__
inline int GetMaxSaturatedSetResultForTesting() {
// For ARM Asm version the set function maxes out to the biggest
// possible integer part with the fractional part zero'd out.
// e.g. 0x7fffffc0.
return LayoutUnit::kRawValueMax & ~(LayoutUnit::kFixedPointDenominator - 1);
}
inline int GetMinSaturatedSetResultForTesting() {
return LayoutUnit::kRawValueMin;
}
#else
ALWAYS_INLINE int GetMaxSaturatedSetResultForTesting() {
// For C version the set function maxes out to max int, this differs from
// the ARM asm version.
return LayoutUnit::kRawValueMax;
}
ALWAYS_INLINE int GetMinSaturatedSetResultForTesting() {
return LayoutUnit::kRawValueMin;
}
#endif // CPU(ARM) && COMPILER(GCC)
template <unsigned fractional_bits, typename RawValue>
PLATFORM_EXPORT std::ostream& operator<<(
std::ostream&,
const FixedPoint<fractional_bits, RawValue>&);
} // namespace blink
WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS(blink::LayoutUnit)
#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_LAYOUT_UNIT_H_