1 /*
2 * Copyright 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #pragma once
18
19 #include <algorithm>
20 #include <cstdint>
21 #include <limits>
22 #include <ostream>
23 #include <type_traits>
24 #include <utility>
25
26 namespace android {
27 namespace ui {
28
29 // Forward declare a few things.
30 struct Size;
31 bool operator==(const Size& lhs, const Size& rhs);
32
33 /**
34 * A simple value type representing a two-dimensional size
35 */
36 struct Size {
37 int32_t width;
38 int32_t height;
39
40 // Special values
41 static const Size INVALID;
42 static const Size EMPTY;
43
44 // ------------------------------------------------------------------------
45 // Construction
46 // ------------------------------------------------------------------------
47
SizeSize48 Size() : Size(INVALID) {}
49 template <typename T>
SizeSize50 Size(T&& w, T&& h)
51 : width(Size::clamp<int32_t, T>(std::forward<T>(w))),
52 height(Size::clamp<int32_t, T>(std::forward<T>(h))) {}
53
54 // ------------------------------------------------------------------------
55 // Accessors
56 // ------------------------------------------------------------------------
57
getWidthSize58 int32_t getWidth() const { return width; }
getHeightSize59 int32_t getHeight() const { return height; }
60
61 template <typename T>
setWidthSize62 void setWidth(T&& v) {
63 width = Size::clamp<int32_t, T>(std::forward<T>(v));
64 }
65 template <typename T>
setHeightSize66 void setHeight(T&& v) {
67 height = Size::clamp<int32_t, T>(std::forward<T>(v));
68 }
69
70 // ------------------------------------------------------------------------
71 // Assignment
72 // ------------------------------------------------------------------------
73
setSize74 void set(const Size& size) { *this = size; }
75 template <typename T>
setSize76 void set(T&& w, T&& h) {
77 set(Size(std::forward<T>(w), std::forward<T>(h)));
78 }
79
80 // Sets the value to INVALID
makeInvalidSize81 void makeInvalid() { set(INVALID); }
82
83 // Sets the value to EMPTY
clearSize84 void clear() { set(EMPTY); }
85
86 // ------------------------------------------------------------------------
87 // Semantic checks
88 // ------------------------------------------------------------------------
89
90 // Valid means non-negative width and height
isValidSize91 bool isValid() const { return width >= 0 && height >= 0; }
92
93 // Empty means zero width and height
isEmptySize94 bool isEmpty() const { return *this == EMPTY; }
95
96 // ------------------------------------------------------------------------
97 // Clamp Helpers
98 // ------------------------------------------------------------------------
99
100 // Note: We use only features available in C++11 here for compatibility with
101 // external targets which include this file directly or indirectly and which
102 // themselves use C++11.
103
104 // C++11 compatible replacement for std::remove_cv_reference_t [C++20]
105 template <typename T>
106 using remove_cv_reference_t =
107 typename std::remove_cv<typename std::remove_reference<T>::type>::type;
108
109 // Takes a value of type FromType, and ensures it can be represented as a value of type ToType,
110 // clamping the input value to the output range if necessary.
111 template <typename ToType, typename FromType>
112 static Size::remove_cv_reference_t<ToType>
clampSize113 clamp(typename std::enable_if<
114 std::numeric_limits<Size::remove_cv_reference_t<ToType>>::is_specialized &&
115 std::numeric_limits<Size::remove_cv_reference_t<FromType>>::is_specialized,
116 FromType>::type v) {
117 using BareToType = remove_cv_reference_t<ToType>;
118 using BareFromType = remove_cv_reference_t<FromType>;
119 static constexpr auto toHighest = std::numeric_limits<BareToType>::max();
120 static constexpr auto toLowest = std::numeric_limits<BareToType>::lowest();
121 static constexpr auto fromHighest = std::numeric_limits<BareFromType>::max();
122 static constexpr auto fromLowest = std::numeric_limits<BareFromType>::lowest();
123
124 // Get the closest representation of [toLowest, toHighest] in type
125 // FromType to use to clamp the input value before conversion.
126
127 // std::common_type<...> is used to get a value-preserving type for the
128 // top end of the range.
129 using CommonHighestType = std::common_type_t<BareToType, BareFromType>;
130
131 // std::make_signed<std::common_type<...>> is used to get a
132 // value-preserving type for the bottom end of the range, except this is
133 // a bit trickier for non-integer types like float.
134 using CommonLowestType =
135 std::conditional_t<std::numeric_limits<CommonHighestType>::is_integer,
136 std::make_signed_t<std::conditional_t<
137 std::numeric_limits<CommonHighestType>::is_integer,
138 CommonHighestType, int /* not used */>>,
139 CommonHighestType>;
140
141 // We can then compute the clamp range in a way that can be later
142 // trivially converted to either the 'from' or 'to' types, and be
143 // representabile in either.
144 static constexpr auto commonClampHighest =
145 std::min(static_cast<CommonHighestType>(fromHighest),
146 static_cast<CommonHighestType>(toHighest));
147 static constexpr auto commonClampLowest =
148 std::max(static_cast<CommonLowestType>(fromLowest),
149 static_cast<CommonLowestType>(toLowest));
150
151 static constexpr auto fromClampHighest = static_cast<BareFromType>(commonClampHighest);
152 static constexpr auto fromClampLowest = static_cast<BareFromType>(commonClampLowest);
153
154 // A clamp is needed only if the range we are clamping to is not the
155 // same as the range of the input.
156 static constexpr bool isClampNeeded =
157 (fromLowest != fromClampLowest) || (fromHighest != fromClampHighest);
158
159 // If a clamp is not needed, the conversion is just a trivial cast.
160 if (!isClampNeeded) {
161 return static_cast<BareToType>(v);
162 }
163
164 // Note: Clang complains about the value of INT32_MAX not being
165 // convertible back to int32_t from float if this is made "constexpr",
166 // when clamping a float value to an int32_t value. This is however
167 // covered by a test case to ensure the run-time cast works correctly.
168 const auto toClampHighest = static_cast<BareToType>(commonClampHighest);
169 const auto toClampLowest = static_cast<BareToType>(commonClampLowest);
170
171 // Otherwise clamping is done by using the already computed endpoints
172 // for each type.
173 return (v <= fromClampLowest)
174 ? toClampLowest
175 : ((v >= fromClampHighest) ? toClampHighest : static_cast<BareToType>(v));
176 }
177 };
178
179 // ------------------------------------------------------------------------
180 // Comparisons
181 // ------------------------------------------------------------------------
182
183 inline bool operator==(const Size& lhs, const Size& rhs) {
184 return lhs.width == rhs.width && lhs.height == rhs.height;
185 }
186
187 inline bool operator!=(const Size& lhs, const Size& rhs) {
188 return !operator==(lhs, rhs);
189 }
190
191 inline bool operator<(const Size& lhs, const Size& rhs) {
192 // Orders by increasing width, then height.
193 if (lhs.width != rhs.width) return lhs.width < rhs.width;
194 return lhs.height < rhs.height;
195 }
196
197 // Defining PrintTo helps with Google Tests.
PrintTo(const Size & size,::std::ostream * os)198 static inline void PrintTo(const Size& size, ::std::ostream* os) {
199 *os << "Size(" << size.width << ", " << size.height << ")";
200 }
201
202 } // namespace ui
203 } // namespace android
204