Flutter Impeller
saturated_math.h
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef FLUTTER_IMPELLER_GEOMETRY_SATURATED_MATH_H_
6 #define FLUTTER_IMPELLER_GEOMETRY_SATURATED_MATH_H_
7 
8 #include <algorithm>
9 #include <limits>
10 #include <type_traits>
11 
12 #include "flutter/fml/logging.h"
14 
15 namespace impeller {
16 
17 namespace saturated {
18 
19 // NOLINTBEGIN(readability-identifier-naming)
20 template <typename T>
21 inline constexpr bool is_signed_integral_v =
22  std::is_integral_v<T> && std::is_signed_v<T>;
23 // NOLINTEND(readability-identifier-naming)
24 
25 #define ONLY_ON_SIGNED_INT_RET(Type, Ret) \
26  template <typename Type> \
27  constexpr inline std::enable_if_t<is_signed_integral_v<Type>, Ret>
28 #define ONLY_ON_SIGNED_INT(Type) ONLY_ON_SIGNED_INT_RET(Type, Type)
29 
30 #define ONLY_ON_FLOAT_RET(Type, Ret) \
31  template <typename Type> \
32  constexpr inline std::enable_if_t<std::is_floating_point_v<Type>, Ret>
33 #define ONLY_ON_FLOAT(Type) ONLY_ON_FLOAT_RET(Type, Type)
34 
35 #define ONLY_ON_FLOAT_TO_SIGNED_INT_RET(FPType, SIType, Ret) \
36  template <typename FPType, typename SIType> \
37  constexpr inline std::enable_if_t< \
38  std::is_floating_point_v<FPType> && is_signed_integral_v<SIType>, Ret>
39 #define ONLY_ON_FLOAT_TO_SIGNED_INT(FPType, SIType) \
40  ONLY_ON_FLOAT_TO_SIGNED_INT_RET(FPType, SIType, SIType)
41 
42 #define ONLY_ON_DIFFERING_FLOAT_RET(FPType1, FPType2, Ret) \
43  template <typename FPType1, typename FPType2> \
44  constexpr inline std::enable_if_t<std::is_floating_point_v<FPType1> && \
45  std::is_floating_point_v<FPType2> && \
46  !std::is_same_v<FPType1, FPType2>, \
47  Ret>
48 #define ONLY_ON_DIFFERING_FLOAT(FPType1, FPType2) \
49  ONLY_ON_DIFFERING_FLOAT_RET(FPType1, FPType2, FPType2)
50 
51 #define ONLY_ON_SAME_TYPES_RET(Type1, Type2, Ret) \
52  template <typename Type1, typename Type2> \
53  constexpr inline std::enable_if_t<std::is_same_v<Type1, Type2>, Ret>
54 #define ONLY_ON_SAME_TYPES(Type1, Type2) \
55  ONLY_ON_SAME_TYPES_RET(Type1, Type2, Type2)
56 
57 ONLY_ON_SIGNED_INT(SI) Add(SI location, SI distance) {
58  if (location >= 0) {
59  if (distance > std::numeric_limits<SI>::max() - location) {
60  return std::numeric_limits<SI>::max();
61  }
62  } else if (distance < std::numeric_limits<SI>::min() - location) {
63  return std::numeric_limits<SI>::min();
64  }
65  return location + distance;
66 }
67 
68 ONLY_ON_FLOAT(FP) Add(FP location, FP distance) {
69  return location + distance;
70 }
71 
72 ONLY_ON_SIGNED_INT(SI) Sub(SI upper, SI lower) {
73  if (upper >= 0) {
74  if (lower < 0 && upper > std::numeric_limits<SI>::max() + lower) {
75  return std::numeric_limits<SI>::max();
76  }
77  } else if (lower > 0 && upper < std::numeric_limits<SI>::min() + lower) {
78  return std::numeric_limits<SI>::min();
79  }
80  return upper - lower;
81 }
82 
83 ONLY_ON_FLOAT(FP) Sub(FP upper, FP lower) {
84  return upper - lower;
85 }
86 
87 ONLY_ON_SIGNED_INT_RET(SI, Scalar) AverageScalar(SI a, SI b) {
88  // scalbn has an implementation for ints that converts to double
89  // while adjusting the exponent.
90  return static_cast<Scalar>(std::scalbn(a, -1) + std::scalbn(b, -1));
91 }
92 
93 ONLY_ON_FLOAT(FP) AverageScalar(FP a, FP b) {
94  // GetCenter might want this to return 0 for a Maximum Rect, but it
95  // will currently produce NaN instead. For the Maximum Rect itself
96  // a 0 would make sense as the center, but for a computed rect that
97  // incidentally ended up with infinities, NaN may be a better choice.
98  // return static_cast<Scalar>(std::scalbn(a, -1) + std::scalbn(b, -1));
99 
100  // This equation would save an extra scalbn operation but at the cost
101  // of having very large (or very neagive) a's and b's overflow to
102  // +/- infinity. Scaling first allows finite numbers to be more likely
103  // to have a finite average.
104  // return std::scalbn(a + b, -1);
105 
106  return static_cast<Scalar>(std::scalbn(a, -1) + std::scalbn(b, -1));
107 }
108 
109 ONLY_ON_SAME_TYPES(T, U) Cast(T v) {
110  return v;
111 }
112 
113 ONLY_ON_FLOAT_TO_SIGNED_INT(FP, SI) Cast(FP v) {
114  if (v <= static_cast<FP>(std::numeric_limits<SI>::min())) {
115  return std::numeric_limits<SI>::min();
116  } else if (v >= static_cast<FP>(std::numeric_limits<SI>::max())) {
117  return std::numeric_limits<SI>::max();
118  }
119  return static_cast<SI>(v);
120 }
121 
122 ONLY_ON_DIFFERING_FLOAT(FP1, FP2) Cast(FP1 v) {
123  if (std::isfinite(v)) {
124  // Avoid truncation to inf/-inf.
125  return std::clamp(static_cast<FP2>(v), //
126  std::numeric_limits<FP2>::lowest(),
127  std::numeric_limits<FP2>::max());
128  } else {
129  return static_cast<FP2>(v);
130  }
131 }
132 
133 #undef ONLY_ON_SAME_TYPES
134 #undef ONLY_ON_SAME_TYPES_RET
135 #undef ONLY_ON_DIFFERING_FLOAT
136 #undef ONLY_ON_DIFFERING_FLOAT_RET
137 #undef ONLY_ON_FLOAT_TO_SIGNED_INT
138 #undef ONLY_ON_FLOAT_TO_SIGNED_INT_RET
139 #undef ONLY_ON_FLOAT
140 #undef ONLY_ON_FLOAT_RET
141 #undef ONLY_ON_SIGNED_INT
142 #undef ONLY_ON_SIGNED_INT_RET
143 
144 } // namespace saturated
145 
146 } // namespace impeller
147 
148 #endif // FLUTTER_IMPELLER_GEOMETRY_SATURATED_MATH_H_
ONLY_ON_SAME_TYPES(T, U) Cast(T v)
ONLY_ON_SIGNED_INT(SI) Add(SI location
ONLY_ON_DIFFERING_FLOAT(FP1, FP2) Cast(FP1 v)
ONLY_ON_FLOAT_TO_SIGNED_INT(FP, SI) Cast(FP v)
ONLY_ON_FLOAT(FP) Add(FP location
constexpr bool is_signed_integral_v
ONLY_ON_SIGNED_INT_RET(SI, Scalar) AverageScalar(SI a
float Scalar
Definition: scalar.h:19