blob: bd92cbf42474c7e69df64959813402a9cdb6c157 [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef FLUTTER_IMPELLER_GEOMETRY_SATURATED_MATH_H_
#define FLUTTER_IMPELLER_GEOMETRY_SATURATED_MATH_H_
#include <algorithm>
#include <limits>
#include <type_traits>
#include "flutter/fml/logging.h"
#include "impeller/geometry/scalar.h"
namespace impeller {
namespace saturated {
// NOLINTBEGIN(readability-identifier-naming)
template <typename T>
inline constexpr bool is_signed_integral_v =
std::is_integral_v<T> && std::is_signed_v<T>;
// NOLINTEND(readability-identifier-naming)
#define ONLY_ON_SIGNED_INT_RET(Type, Ret) \
template <typename Type> \
constexpr inline std::enable_if_t<is_signed_integral_v<Type>, Ret>
#define ONLY_ON_SIGNED_INT(Type) ONLY_ON_SIGNED_INT_RET(Type, Type)
#define ONLY_ON_FLOAT_RET(Type, Ret) \
template <typename Type> \
constexpr inline std::enable_if_t<std::is_floating_point_v<Type>, Ret>
#define ONLY_ON_FLOAT(Type) ONLY_ON_FLOAT_RET(Type, Type)
#define ONLY_ON_FLOAT_TO_SIGNED_INT_RET(FPType, SIType, Ret) \
template <typename FPType, typename SIType> \
constexpr inline std::enable_if_t< \
std::is_floating_point_v<FPType> && is_signed_integral_v<SIType>, Ret>
#define ONLY_ON_FLOAT_TO_SIGNED_INT(FPType, SIType) \
ONLY_ON_FLOAT_TO_SIGNED_INT_RET(FPType, SIType, SIType)
#define ONLY_ON_DIFFERING_FLOAT_RET(FPType1, FPType2, Ret) \
template <typename FPType1, typename FPType2> \
constexpr inline std::enable_if_t<std::is_floating_point_v<FPType1> && \
std::is_floating_point_v<FPType2> && \
!std::is_same_v<FPType1, FPType2>, \
Ret>
#define ONLY_ON_DIFFERING_FLOAT(FPType1, FPType2) \
ONLY_ON_DIFFERING_FLOAT_RET(FPType1, FPType2, FPType2)
#define ONLY_ON_SAME_TYPES_RET(Type1, Type2, Ret) \
template <typename Type1, typename Type2> \
constexpr inline std::enable_if_t<std::is_same_v<Type1, Type2>, Ret>
#define ONLY_ON_SAME_TYPES(Type1, Type2) \
ONLY_ON_SAME_TYPES_RET(Type1, Type2, Type2)
ONLY_ON_SIGNED_INT(SI) Add(SI location, SI distance) {
if (location >= 0) {
if (distance > std::numeric_limits<SI>::max() - location) {
return std::numeric_limits<SI>::max();
}
} else if (distance < std::numeric_limits<SI>::min() - location) {
return std::numeric_limits<SI>::min();
}
return location + distance;
}
ONLY_ON_FLOAT(FP) Add(FP location, FP distance) {
return location + distance;
}
ONLY_ON_SIGNED_INT(SI) Sub(SI upper, SI lower) {
if (upper >= 0) {
if (lower < 0 && upper > std::numeric_limits<SI>::max() + lower) {
return std::numeric_limits<SI>::max();
}
} else if (lower > 0 && upper < std::numeric_limits<SI>::min() + lower) {
return std::numeric_limits<SI>::min();
}
return upper - lower;
}
ONLY_ON_FLOAT(FP) Sub(FP upper, FP lower) {
return upper - lower;
}
ONLY_ON_SIGNED_INT_RET(SI, Scalar) AverageScalar(SI a, SI b) {
// scalbn has an implementation for ints that converts to double
// while adjusting the exponent.
return static_cast<Scalar>(std::scalbn(a, -1) + std::scalbn(b, -1));
}
ONLY_ON_FLOAT(FP) AverageScalar(FP a, FP b) {
// GetCenter might want this to return 0 for a Maximum Rect, but it
// will currently produce NaN instead. For the Maximum Rect itself
// a 0 would make sense as the center, but for a computed rect that
// incidentally ended up with infinities, NaN may be a better choice.
// return static_cast<Scalar>(std::scalbn(a, -1) + std::scalbn(b, -1));
// This equation would save an extra scalbn operation but at the cost
// of having very large (or very neagive) a's and b's overflow to
// +/- infinity. Scaling first allows finite numbers to be more likely
// to have a finite average.
// return std::scalbn(a + b, -1);
return static_cast<Scalar>(std::scalbn(a, -1) + std::scalbn(b, -1));
}
ONLY_ON_SAME_TYPES(T, U) Cast(T v) {
return v;
}
ONLY_ON_FLOAT_TO_SIGNED_INT(FP, SI) Cast(FP v) {
if (v <= static_cast<FP>(std::numeric_limits<SI>::min())) {
return std::numeric_limits<SI>::min();
} else if (v >= static_cast<FP>(std::numeric_limits<SI>::max())) {
return std::numeric_limits<SI>::max();
}
return static_cast<SI>(v);
}
ONLY_ON_DIFFERING_FLOAT(FP1, FP2) Cast(FP1 v) {
if (std::isfinite(v)) {
// Avoid truncation to inf/-inf.
return std::clamp(static_cast<FP2>(v), //
std::numeric_limits<FP2>::lowest(),
std::numeric_limits<FP2>::max());
} else {
return static_cast<FP2>(v);
}
}
#undef ONLY_ON_SAME_TYPES
#undef ONLY_ON_SAME_TYPES_RET
#undef ONLY_ON_DIFFERING_FLOAT
#undef ONLY_ON_DIFFERING_FLOAT_RET
#undef ONLY_ON_FLOAT_TO_SIGNED_INT
#undef ONLY_ON_FLOAT_TO_SIGNED_INT_RET
#undef ONLY_ON_FLOAT
#undef ONLY_ON_FLOAT_RET
#undef ONLY_ON_SIGNED_INT
#undef ONLY_ON_SIGNED_INT_RET
} // namespace saturated
} // namespace impeller
#endif // FLUTTER_IMPELLER_GEOMETRY_SATURATED_MATH_H_