/* * Copyright 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include <algorithm> #include <cstdint> #include <limits> #include <type_traits> #include <utility> namespace android { namespace ui { // Forward declare a few things. struct Size; bool operator==(const Size& lhs, const Size& rhs); /** * A simple value type representing a two-dimensional size */ struct Size { int32_t width; int32_t height; // Special values static const Size INVALID; static const Size EMPTY; // ------------------------------------------------------------------------ // Construction // ------------------------------------------------------------------------ Size() : Size(INVALID) {} template <typename T> Size(T&& w, T&& h) : width(Size::clamp<int32_t, T>(std::forward<T>(w))), height(Size::clamp<int32_t, T>(std::forward<T>(h))) {} // ------------------------------------------------------------------------ // Accessors // ------------------------------------------------------------------------ int32_t getWidth() const { return width; } int32_t getHeight() const { return height; } template <typename T> void setWidth(T&& v) { width = Size::clamp<int32_t, T>(std::forward<T>(v)); } template <typename T> void setHeight(T&& v) { height = Size::clamp<int32_t, T>(std::forward<T>(v)); } // ------------------------------------------------------------------------ // Assignment // ------------------------------------------------------------------------ void set(const Size& size) { *this = size; } template <typename T> void set(T&& w, T&& h) { set(Size(std::forward<T>(w), std::forward<T>(h))); } // Sets the value to INVALID void makeInvalid() { set(INVALID); } // Sets the value to EMPTY void clear() { set(EMPTY); } // ------------------------------------------------------------------------ // Semantic checks // ------------------------------------------------------------------------ // Valid means non-negative width and height bool isValid() const { return width >= 0 && height >= 0; } // Empty means zero width and height bool isEmpty() const { return *this == EMPTY; } // ------------------------------------------------------------------------ // Clamp Helpers // ------------------------------------------------------------------------ // Note: We use only features available in C++11 here for compatibility with // external targets which include this file directly or indirectly and which // themselves use C++11. // C++11 compatible replacement for std::remove_cv_reference_t [C++20] template <typename T> using remove_cv_reference_t = typename std::remove_cv<typename std::remove_reference<T>::type>::type; // Takes a value of type FromType, and ensures it can be represented as a value of type ToType, // clamping the input value to the output range if necessary. template <typename ToType, typename FromType> static Size::remove_cv_reference_t<ToType> clamp( typename std::enable_if< std::numeric_limits<Size::remove_cv_reference_t<ToType>>::is_bounded && std::numeric_limits<Size::remove_cv_reference_t<FromType>>::is_bounded, FromType&&>::type v) { static constexpr auto toHighest = std::numeric_limits<remove_cv_reference_t<ToType>>::max(); static constexpr auto toLowest = std::numeric_limits<remove_cv_reference_t<ToType>>::lowest(); static constexpr auto fromHighest = std::numeric_limits<remove_cv_reference_t<FromType>>::max(); static constexpr auto fromLowest = std::numeric_limits<remove_cv_reference_t<FromType>>::lowest(); // A clamp is needed if the range of FromType is not a subset of the range of ToType static constexpr bool isClampNeeded = (toLowest > fromLowest) || (toHighest < fromHighest); // If a clamp is not needed, the conversion is just a trivial cast. if (!isClampNeeded) { return static_cast<ToType>(v); } // Otherwise we leverage implicit conversion to safely compare values of // different types, to ensure we return a value clamped to the range of // ToType. return v < toLowest ? toLowest : (v > toHighest ? toHighest : static_cast<ToType>(v)); } }; // ------------------------------------------------------------------------ // Comparisons // ------------------------------------------------------------------------ inline bool operator==(const Size& lhs, const Size& rhs) { return lhs.width == rhs.width && lhs.height == rhs.height; } inline bool operator!=(const Size& lhs, const Size& rhs) { return !operator==(lhs, rhs); } inline bool operator<(const Size& lhs, const Size& rhs) { // Orders by increasing width, then height. if (lhs.width != rhs.width) return lhs.width < rhs.width; return lhs.height < rhs.height; } } // namespace ui } // namespace android