// 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 #include #include #include "flutter/fml/logging.h" #include "impeller/geometry/scalar.h" namespace impeller { namespace saturated { // NOLINTBEGIN(readability-identifier-naming) template inline constexpr bool is_signed_integral_v = std::is_integral_v && std::is_signed_v; // NOLINTEND(readability-identifier-naming) #define ONLY_ON_SIGNED_INT_RET(Type, Ret) \ template \ constexpr inline std::enable_if_t, Ret> #define ONLY_ON_SIGNED_INT(Type) ONLY_ON_SIGNED_INT_RET(Type, Type) #define ONLY_ON_FLOAT_RET(Type, Ret) \ template \ constexpr inline std::enable_if_t, Ret> #define ONLY_ON_FLOAT(Type) ONLY_ON_FLOAT_RET(Type, Type) #define ONLY_ON_FLOAT_TO_SIGNED_INT_RET(FPType, SIType, Ret) \ template \ constexpr inline std::enable_if_t< \ std::is_floating_point_v && is_signed_integral_v, 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 \ constexpr inline std::enable_if_t && \ std::is_floating_point_v && \ !std::is_same_v, \ 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 \ constexpr inline std::enable_if_t, 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::max() - location) { return std::numeric_limits::max(); } } else if (distance < std::numeric_limits::min() - location) { return std::numeric_limits::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::max() + lower) { return std::numeric_limits::max(); } } else if (lower > 0 && upper < std::numeric_limits::min() + lower) { return std::numeric_limits::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(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(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(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(std::numeric_limits::min())) { return std::numeric_limits::min(); } else if (v >= static_cast(std::numeric_limits::max())) { return std::numeric_limits::max(); } return static_cast(v); } ONLY_ON_DIFFERING_FLOAT(FP1, FP2) Cast(FP1 v) { if (std::isfinite(v)) { // Avoid truncation to inf/-inf. return std::clamp(static_cast(v), // std::numeric_limits::lowest(), std::numeric_limits::max()); } else { return static_cast(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_