/*
* 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