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 #include <ui/Rotation.h>
27 
28 namespace android::ui {
29 
30 // A simple value type representing a two-dimensional size.
31 struct Size {
32     int32_t width = -1;
33     int32_t height = -1;
34 
35     constexpr Size() = default;
36 
37     template <typename T>
SizeSize38     constexpr Size(T w, T h) : width(clamp<int32_t>(w)), height(clamp<int32_t>(h)) {}
39 
getWidthSize40     int32_t getWidth() const { return width; }
getHeightSize41     int32_t getHeight() const { return height; }
42 
43     // Valid means non-negative width and height
isValidSize44     bool isValid() const { return width >= 0 && height >= 0; }
45 
46     // Empty means zero width and height
47     bool isEmpty() const;
48 
49     template <typename T>
setWidthSize50     void setWidth(T v) {
51         width = clamp<int32_t>(v);
52     }
53 
54     template <typename T>
setHeightSize55     void setHeight(T v) {
56         height = clamp<int32_t>(v);
57     }
58 
setSize59     void set(Size size) { *this = size; }
60 
61     template <typename T>
setSize62     void set(T w, T h) {
63         set(Size(w, h));
64     }
65 
66     // Applies a rotation onto the size
rotateSize67     void rotate(Rotation rotation) {
68         if (rotation == ROTATION_90 || rotation == ROTATION_270) {
69             transpose();
70         }
71     }
72 
73     // Swaps the width and height, emulating a 90 degree rotation.
transposeSize74     void transpose() { std::swap(width, height); }
75 
76     // Sets the value to kInvalidSize
77     void makeInvalid();
78 
79     // Sets the value to kEmptySize
80     void clear();
81 
82     // TODO: Replace with std::remove_cvref_t in C++20.
83     template <typename T>
84     using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<T>>;
85 
86     // Takes a value of type FromType, and ensures it can be represented as a value of type ToType,
87     // clamping the input value to the output range if necessary.
88     template <typename ToType, typename FromType>
clampSize89     static constexpr remove_cvref_t<ToType> clamp(FromType v) {
90         using BareToType = remove_cvref_t<ToType>;
91         using ToLimits = std::numeric_limits<BareToType>;
92 
93         using BareFromType = remove_cvref_t<FromType>;
94         using FromLimits = std::numeric_limits<BareFromType>;
95 
96         static_assert(ToLimits::is_specialized && FromLimits::is_specialized);
97 
98         constexpr auto toHighest = ToLimits::max();
99         constexpr auto toLowest = ToLimits::lowest();
100         constexpr auto fromHighest = FromLimits::max();
101         constexpr auto fromLowest = FromLimits::lowest();
102 
103         // Get the closest representation of [toLowest, toHighest] in type
104         // FromType to use to clamp the input value before conversion.
105 
106         // std::common_type<...> is used to get a value-preserving type for the
107         // top end of the range.
108         using CommonHighestType = std::common_type_t<BareToType, BareFromType>;
109         using CommonLimits = std::numeric_limits<CommonHighestType>;
110 
111         // std::make_signed<std::common_type<...>> is used to get a
112         // value-preserving type for the bottom end of the range, except this is
113         // a bit trickier for non-integer types like float.
114         using CommonLowestType = std::conditional_t<
115                 CommonLimits::is_integer,
116                 std::make_signed_t<std::conditional_t<CommonLimits::is_integer, CommonHighestType,
117                                                       int /* not used */>>,
118                 CommonHighestType>;
119 
120         // We can then compute the clamp range in a way that can be later
121         // trivially converted to either the 'from' or 'to' types, and be
122         // representable in either.
123         constexpr auto commonClampHighest = std::min(static_cast<CommonHighestType>(fromHighest),
124                                                      static_cast<CommonHighestType>(toHighest));
125         constexpr auto commonClampLowest = std::max(static_cast<CommonLowestType>(fromLowest),
126                                                     static_cast<CommonLowestType>(toLowest));
127 
128         constexpr auto fromClampHighest = static_cast<BareFromType>(commonClampHighest);
129         constexpr auto fromClampLowest = static_cast<BareFromType>(commonClampLowest);
130 
131         // A clamp is needed only if the range we are clamping to is not the
132         // same as the range of the input.
133         constexpr bool isClampNeeded =
134                 (fromLowest != fromClampLowest) || (fromHighest != fromClampHighest);
135 
136         // If a clamp is not needed, the conversion is just a trivial cast.
137         if constexpr (!isClampNeeded) {
138             return static_cast<BareToType>(v);
139         }
140 
141         // Note: Clang complains about the value of INT32_MAX not being
142         // convertible back to int32_t from float if this is made "constexpr",
143         // when clamping a float value to an int32_t value. This is however
144         // covered by a test case to ensure the run-time cast works correctly.
145         const auto toClampHighest = static_cast<BareToType>(commonClampHighest);
146         const auto toClampLowest = static_cast<BareToType>(commonClampLowest);
147 
148         // Otherwise clamping is done by using the already computed endpoints
149         // for each type.
150         if (v <= fromClampLowest) {
151             return toClampLowest;
152         }
153 
154         return v >= fromClampHighest ? toClampHighest : static_cast<BareToType>(v);
155     }
156 };
157 
158 constexpr Size kInvalidSize;
159 constexpr Size kEmptySize{0, 0};
160 
makeInvalid()161 inline void Size::makeInvalid() {
162     set(kInvalidSize);
163 }
164 
clear()165 inline void Size::clear() {
166     set(kEmptySize);
167 }
168 
169 inline bool operator==(Size lhs, Size rhs) {
170     return lhs.width == rhs.width && lhs.height == rhs.height;
171 }
172 
isEmpty()173 inline bool Size::isEmpty() const {
174     return *this == kEmptySize;
175 }
176 
177 inline bool operator!=(Size lhs, Size rhs) {
178     return !(lhs == rhs);
179 }
180 
181 inline bool operator<(Size lhs, Size rhs) {
182     // Orders by increasing width, then height.
183     if (lhs.width != rhs.width) return lhs.width < rhs.width;
184     return lhs.height < rhs.height;
185 }
186 
187 // Defining PrintTo helps with Google Tests.
PrintTo(Size size,std::ostream * stream)188 inline void PrintTo(Size size, std::ostream* stream) {
189     *stream << "Size(" << size.width << ", " << size.height << ')';
190 }
191 
192 } // namespace android::ui
193