• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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