diff options
Diffstat (limited to 'gfx/2d/RectAbsolute.h')
-rw-r--r-- | gfx/2d/RectAbsolute.h | 305 |
1 files changed, 305 insertions, 0 deletions
diff --git a/gfx/2d/RectAbsolute.h b/gfx/2d/RectAbsolute.h new file mode 100644 index 0000000000..09da87c80f --- /dev/null +++ b/gfx/2d/RectAbsolute.h @@ -0,0 +1,305 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_GFX_RECT_ABSOLUTE_H_ +#define MOZILLA_GFX_RECT_ABSOLUTE_H_ + +#include <algorithm> +#include <cstdint> + +#include "mozilla/Attributes.h" +#include "Point.h" +#include "Rect.h" +#include "Types.h" + +namespace mozilla { + +template <typename> +struct IsPixel; + +namespace gfx { + +/** + * A RectAbsolute is similar to a Rect (see BaseRect.h), but represented as + * (x1, y1, x2, y2) instead of (x, y, width, height). + * + * Unless otherwise indicated, methods on this class correspond + * to methods on BaseRect. + * + * The API is currently very bare-bones; it may be extended as needed. + * + * Do not use this class directly. Subclass it, pass that subclass as the + * Sub parameter, and only use that subclass. + */ +template <class T, class Sub, class Point, class Rect> +struct BaseRectAbsolute { + protected: + T left, top, right, bottom; + + public: + BaseRectAbsolute() : left(0), top(0), right(0), bottom(0) {} + BaseRectAbsolute(T aLeft, T aTop, T aRight, T aBottom) + : left(aLeft), top(aTop), right(aRight), bottom(aBottom) {} + + MOZ_ALWAYS_INLINE T X() const { return left; } + MOZ_ALWAYS_INLINE T Y() const { return top; } + MOZ_ALWAYS_INLINE T Width() const { return right - left; } + MOZ_ALWAYS_INLINE T Height() const { return bottom - top; } + MOZ_ALWAYS_INLINE T XMost() const { return right; } + MOZ_ALWAYS_INLINE T YMost() const { return bottom; } + MOZ_ALWAYS_INLINE const T& Left() const { return left; } + MOZ_ALWAYS_INLINE const T& Right() const { return right; } + MOZ_ALWAYS_INLINE const T& Top() const { return top; } + MOZ_ALWAYS_INLINE const T& Bottom() const { return bottom; } + MOZ_ALWAYS_INLINE T& Left() { return left; } + MOZ_ALWAYS_INLINE T& Right() { return right; } + MOZ_ALWAYS_INLINE T& Top() { return top; } + MOZ_ALWAYS_INLINE T& Bottom() { return bottom; } + T Area() const { return Width() * Height(); } + + void Inflate(T aD) { Inflate(aD, aD); } + void Inflate(T aDx, T aDy) { + left -= aDx; + top -= aDy; + right += aDx; + bottom += aDy; + } + + MOZ_ALWAYS_INLINE void SetBox(T aLeft, T aTop, T aRight, T aBottom) { + left = aLeft; + top = aTop; + right = aRight; + bottom = aBottom; + } + void SetLeftEdge(T aLeft) { left = aLeft; } + void SetRightEdge(T aRight) { right = aRight; } + void SetTopEdge(T aTop) { top = aTop; } + void SetBottomEdge(T aBottom) { bottom = aBottom; } + + static Sub FromRect(const Rect& aRect) { + if (aRect.Overflows()) { + return Sub(); + } + return Sub(aRect.x, aRect.y, aRect.XMost(), aRect.YMost()); + } + + [[nodiscard]] Sub Intersect(const Sub& aOther) const { + Sub result; + result.left = std::max<T>(left, aOther.left); + result.top = std::max<T>(top, aOther.top); + result.right = std::min<T>(right, aOther.right); + result.bottom = std::min<T>(bottom, aOther.bottom); + if (result.right < result.left || result.bottom < result.top) { + result.SizeTo(0, 0); + } + return result; + } + + bool IsEmpty() const { return right <= left || bottom <= top; } + + bool IsEqualEdges(const Sub& aOther) const { + return left == aOther.left && top == aOther.top && right == aOther.right && + bottom == aOther.bottom; + } + + bool IsEqualInterior(const Sub& aRect) const { + return IsEqualEdges(aRect) || (IsEmpty() && aRect.IsEmpty()); + } + + MOZ_ALWAYS_INLINE void MoveBy(T aDx, T aDy) { + left += aDx; + right += aDx; + top += aDy; + bottom += aDy; + } + MOZ_ALWAYS_INLINE void MoveBy(const Point& aPoint) { + left += aPoint.x; + right += aPoint.x; + top += aPoint.y; + bottom += aPoint.y; + } + MOZ_ALWAYS_INLINE void SizeTo(T aWidth, T aHeight) { + right = left + aWidth; + bottom = top + aHeight; + } + + bool Contains(const Sub& aRect) const { + return aRect.IsEmpty() || (left <= aRect.left && aRect.right <= right && + top <= aRect.top && aRect.bottom <= bottom); + } + bool Contains(T aX, T aY) const { + return (left <= aX && aX < right && top <= aY && aY < bottom); + } + + bool Intersects(const Sub& aRect) const { + return !IsEmpty() && !aRect.IsEmpty() && left < aRect.right && + aRect.left < right && top < aRect.bottom && aRect.top < bottom; + } + + void SetEmpty() { left = right = top = bottom = 0; } + + // Returns the smallest rectangle that contains both the area of both + // this and aRect. Thus, empty input rectangles are ignored. + // Note: if both rectangles are empty, returns aRect. + // WARNING! This is not safe against overflow, prefer using SafeUnion instead + // when dealing with int-based rects. + [[nodiscard]] Sub Union(const Sub& aRect) const { + if (IsEmpty()) { + return aRect; + } else if (aRect.IsEmpty()) { + return *static_cast<const Sub*>(this); + } else { + return UnionEdges(aRect); + } + } + // Returns the smallest rectangle that contains both the points (including + // edges) of both aRect1 and aRect2. + // Thus, empty input rectangles are allowed to affect the result. + // WARNING! This is not safe against overflow, prefer using SafeUnionEdges + // instead when dealing with int-based rects. + [[nodiscard]] Sub UnionEdges(const Sub& aRect) const { + Sub result; + result.left = std::min(left, aRect.left); + result.top = std::min(top, aRect.top); + result.right = std::max(XMost(), aRect.XMost()); + result.bottom = std::max(YMost(), aRect.YMost()); + return result; + } + + // Scale 'this' by aScale without doing any rounding. + void Scale(T aScale) { Scale(aScale, aScale); } + // Scale 'this' by aXScale and aYScale, without doing any rounding. + void Scale(T aXScale, T aYScale) { + right = XMost() * aXScale; + bottom = YMost() * aYScale; + left = left * aXScale; + top = top * aYScale; + } + // Scale 'this' by aScale, converting coordinates to integers so that the + // result is the smallest integer-coordinate rectangle containing the + // unrounded result. Note: this can turn an empty rectangle into a non-empty + // rectangle + void ScaleRoundOut(double aScale) { ScaleRoundOut(aScale, aScale); } + // Scale 'this' by aXScale and aYScale, converting coordinates to integers so + // that the result is the smallest integer-coordinate rectangle containing the + // unrounded result. + // Note: this can turn an empty rectangle into a non-empty rectangle + void ScaleRoundOut(double aXScale, double aYScale) { + right = static_cast<T>(ceil(double(XMost()) * aXScale)); + bottom = static_cast<T>(ceil(double(YMost()) * aYScale)); + left = static_cast<T>(floor(double(left) * aXScale)); + top = static_cast<T>(floor(double(top) * aYScale)); + } + // Scale 'this' by aScale, converting coordinates to integers so that the + // result is the largest integer-coordinate rectangle contained by the + // unrounded result. + void ScaleRoundIn(double aScale) { ScaleRoundIn(aScale, aScale); } + // Scale 'this' by aXScale and aYScale, converting coordinates to integers so + // that the result is the largest integer-coordinate rectangle contained by + // the unrounded result. + void ScaleRoundIn(double aXScale, double aYScale) { + right = static_cast<T>(floor(double(XMost()) * aXScale)); + bottom = static_cast<T>(floor(double(YMost()) * aYScale)); + left = static_cast<T>(ceil(double(left) * aXScale)); + top = static_cast<T>(ceil(double(top) * aYScale)); + } + // Scale 'this' by 1/aScale, converting coordinates to integers so that the + // result is the smallest integer-coordinate rectangle containing the + // unrounded result. Note: this can turn an empty rectangle into a non-empty + // rectangle + void ScaleInverseRoundOut(double aScale) { + ScaleInverseRoundOut(aScale, aScale); + } + // Scale 'this' by 1/aXScale and 1/aYScale, converting coordinates to integers + // so that the result is the smallest integer-coordinate rectangle containing + // the unrounded result. Note: this can turn an empty rectangle into a + // non-empty rectangle + void ScaleInverseRoundOut(double aXScale, double aYScale) { + right = static_cast<T>(ceil(double(XMost()) / aXScale)); + bottom = static_cast<T>(ceil(double(YMost()) / aYScale)); + left = static_cast<T>(floor(double(left) / aXScale)); + top = static_cast<T>(floor(double(top) / aYScale)); + } + // Scale 'this' by 1/aScale, converting coordinates to integers so that the + // result is the largest integer-coordinate rectangle contained by the + // unrounded result. + void ScaleInverseRoundIn(double aScale) { + ScaleInverseRoundIn(aScale, aScale); + } + // Scale 'this' by 1/aXScale and 1/aYScale, converting coordinates to integers + // so that the result is the largest integer-coordinate rectangle contained by + // the unrounded result. + void ScaleInverseRoundIn(double aXScale, double aYScale) { + right = static_cast<T>(floor(double(XMost()) / aXScale)); + bottom = static_cast<T>(floor(double(YMost()) / aYScale)); + left = static_cast<T>(ceil(double(left) / aXScale)); + top = static_cast<T>(ceil(double(top) / aYScale)); + } + + /** + * Translate this rectangle to be inside aRect. If it doesn't fit inside + * aRect then the dimensions that don't fit will be shrunk so that they + * do fit. The resulting rect is returned. + */ + [[nodiscard]] Sub MoveInsideAndClamp(const Sub& aRect) const { + T newLeft = std::max(aRect.left, left); + T newTop = std::max(aRect.top, top); + T width = std::min(aRect.Width(), Width()); + T height = std::min(aRect.Height(), Height()); + Sub rect(newLeft, newTop, newLeft + width, newTop + height); + newLeft = std::min(rect.right, aRect.right) - width; + newTop = std::min(rect.bottom, aRect.bottom) - height; + rect.MoveBy(newLeft - rect.left, newTop - rect.top); + return rect; + } + + friend std::ostream& operator<<( + std::ostream& stream, + const BaseRectAbsolute<T, Sub, Point, Rect>& aRect) { + return stream << "(l=" << aRect.left << ", t=" << aRect.top + << ", r=" << aRect.right << ", b=" << aRect.bottom << ')'; + } +}; + +template <class Units> +struct IntRectAbsoluteTyped + : public BaseRectAbsolute<int32_t, IntRectAbsoluteTyped<Units>, + IntPointTyped<Units>, IntRectTyped<Units>>, + public Units { + static_assert(IsPixel<Units>::value, + "'units' must be a coordinate system tag"); + typedef BaseRectAbsolute<int32_t, IntRectAbsoluteTyped<Units>, + IntPointTyped<Units>, IntRectTyped<Units>> + Super; + typedef IntParam<int32_t> ToInt; + + IntRectAbsoluteTyped() : Super() {} + IntRectAbsoluteTyped(ToInt aLeft, ToInt aTop, ToInt aRight, ToInt aBottom) + : Super(aLeft.value, aTop.value, aRight.value, aBottom.value) {} +}; + +template <class Units> +struct RectAbsoluteTyped + : public BaseRectAbsolute<Float, RectAbsoluteTyped<Units>, + PointTyped<Units>, RectTyped<Units>>, + public Units { + static_assert(IsPixel<Units>::value, + "'units' must be a coordinate system tag"); + typedef BaseRectAbsolute<Float, RectAbsoluteTyped<Units>, PointTyped<Units>, + RectTyped<Units>> + Super; + + RectAbsoluteTyped() : Super() {} + RectAbsoluteTyped(Float aLeft, Float aTop, Float aRight, Float aBottom) + : Super(aLeft, aTop, aRight, aBottom) {} +}; + +typedef IntRectAbsoluteTyped<UnknownUnits> IntRectAbsolute; + +} // namespace gfx +} // namespace mozilla + +#endif /* MOZILLA_GFX_RECT_ABSOLUTE_H_ */ |